2 条题解
-
1
暴力
测试点 满足 。
可以使用 的算法。
故,opt为 时,遍历 的字数并修改权值。
opt为 时,遍历 的字数并记录权值为 的点的个数。优化
发现性质:对于一个子数大小为 的节点 ,他的子树中权值为 的点的个数是 。取反其子树时, 会更新为 。于是维护一个
tag数组,表示他是否翻转。每次翻转操作时把 取反(1->0或0->1)。查询时 是 ,虽然我们的修改变成了 ,但查询还是 。可以使用
dfn序在树上装一个线段树来维护权值为 的点的个数。tag数组变为线段数上每一段的翻转懒标记即可。代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m; int a[N],dfn[N],siz[N],b[N],idx; vector<int> g[N]; void dfs(int u,int fa){ dfn[u]=++idx; siz[u]=1; for(auto v:g[u]){ if(v==fa) continue; dfs(v,u); siz[u]+=siz[v]; } } struct SegTree{ #define lc (p<<1) #define rc (p<<1|1) #define mid (l+r>>1) int n; int tag[N<<2],sum[N<<2],cnt[N<<2]; SegTree(int n_){ n=n_; memset(tag,0,sizeof tag); memset(sum,0,sizeof sum); memset(cnt,0,sizeof cnt); } void build(int p,int l,int r){ cnt[p]=r-l+1; if(l==r){ sum[p]=b[l]; return; } build(lc,l,mid); build(rc,mid+1,r); sum[p]=sum[lc]+sum[rc]; } void up(int p){ sum[p]=sum[lc]+sum[rc]; } void down(int p){ if(!tag[p]) return; tag[lc]^=1,tag[rc]^=1; sum[lc]=cnt[lc]-sum[lc]; sum[rc]=cnt[rc]-sum[rc]; tag[p]=0; } void update(int p,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr){ tag[p]^=1; sum[p]=cnt[p]-sum[p]; return; } down(p); if(ql<=mid) update(lc,l,mid,ql,qr); if(qr>mid) update(rc,mid+1,r,ql,qr); up(p); } int query(int p,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr) return sum[p]; down(p); int res=0; if(ql<=mid) res+=query(lc,l,mid,ql,qr); if(qr>mid) res+=query(rc,mid+1,r,ql,qr); return res; } }; int main(){ cin>>n>>m; string s;cin>>s; for(int i=1;i<=n;i++) a[i]=s[i-1]-'0'; for(int i=1;i<n;i++){ int u,v;cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } dfs(1,0); for(int i=1;i<=n;i++) b[dfn[i]]=a[i]; SegTree seg(n); seg.build(1,1,n); while(m--){ int opt,x; cin>>opt>>x; int l=dfn[x],r=dfn[x]+siz[x]-1; if(opt==0) seg.update(1,1,n,l,r); else cout<<seg.query(1,1,n,l,r)<<'\n'; } return 0; }
- 1
信息
- ID
- 2764
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 22
- 已通过
- 12
- 上传者