dsu on tree
-
dsu on tree解决的是什么问题呢?
- 不带修改
- 暴力做法是通过枚举一个点,然后枚举这个点的子树来统计信息来解决
- 例如求每个点的子树里出现了多少种颜色,出现次数最多的颜色等。
-
怎么做呢?
- 先找出轻儿子和重儿子
- dfs的时候先走轻儿子,再走重儿子
- 如果当前轻儿子,回溯回来的时候就把这一次所产生的信息清除。
- 如果当前是重儿子就不清了。
-
复杂度证明?
- 由树链剖分的性质,任意一个节点到根节点的路径上轻边的数量不超过 log n \log n logn条 ,每个节点的信息也就会被统计那么 log n \log n logn 次,因此为 O ( n log n ) O(n\log n) O(nlogn)。
-
代码
-
//cf600E #include<iostream> #include<stdio.h> #include<algorithm> #include<vector> #include<stdlib.h> #include<limits.h> #include<queue> #include<string.h> #include<map> #include<set> #define maxn 100005 #define lson (now<<1) #define rson ((now<<1)|1) #define mid ((nl+nr)>>1) using namespace std; typedef long long ll; int n,tot; int c[maxn]; int head[maxn],nex[maxn<<1],to[maxn<<1]; void add(int x,int y) { to[++tot]=y; nex[tot]=head[x]; head[x]=tot; } int sz[maxn],son[maxn]; void dfs1(int now,int fa) { sz[now]=1; for(int i=head[now];i;i=nex[i]) { if(to[i]!=fa) { dfs1(to[i],now); if(sz[to[i]]>sz[son[now]]) son[now]=to[i]; sz[now]+=sz[to[i]]; } } } ll sum=0; int maxx=0; ll vis[maxn],ans[maxn],cnt[maxn]; void dfs(int now,int fa,int val) { cnt[c[now]]+=val; if(val>0 && cnt[c[now]]>=maxx) { if(cnt[c[now]]>maxx) sum=0,maxx=cnt[c[now]]; sum+=c[now]; } for(int i=head[now];i;i=nex[i]) { if(to[i]!=fa && !vis[to[i]]) { dfs(to[i],now,val); } } } void dfs2(int now,int fa,int isson) { for(int i=head[now];i;i=nex[i]) { if(to[i]!=fa && to[i]!=son[now]) { dfs2(to[i],now,0); } } if(son[now]) { dfs2(son[now],now,1); vis[son[now]]=1; } dfs(now,fa,1); ans[now]=sum; if(son[now]) vis[son[now]]=0; if(!isson) { dfs(now,fa,-1); maxx=sum=0; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1,-1); dfs2(1,-1,0); for(int i=1;i<=n;i++) { printf("%lld%c",ans[i],(i!=n ? ' ' : '\n')); } }
-