CF600E Lomsat gelral(dsu on tree)
题目描述
一个NN个节点的有根树(点11为根),节点从11到NN编号,每个节点有一个颜色C_iCi。
对于一个以xx为根的子树,我们认为颜色cc在这个子树中出现的次数是最多的,则认为cc支配了这个子树。
如果有多个颜色的次数相同并且都为最大,则他们都支配了这个子树。
对每个节点xx,输出所有支配他的子树的颜色的编号之和。
输入格式
第一行一个整数n,
接下来一行n个整数Ci,
接下来n-1行,每行两个整数x,y,表示x和y有一条边。
输出格式
输出一行n个数,第i个数为节点i的答案。
dsu on tree入门题
套到下面的模板里就好辣
void dfs1(int x,int fa){ siz[x]=1; for(auto i:g[x]) if(i!=fa){ dfs1(i,x); siz[x]+=siz[i]; if(siz[i]>siz[big[x]]) big[x]=i;//找重儿子 } } void draw(int x,int fa,int k){ // .... for(auto i:g[x]) if(i!=fa&&!vis[i]) draw(i,x,k); } void dfs2(int x,int fa,bool is){ for(auto i:g[x]) if(i!=fa&&i!=big[x]) dfs2(i,x,0); if(big[x]) dfs2(big[x],x,1),vis[big[x]]=1; draw(x,fa,1); if(big[x]) vis[big[x]]=0; if(!is) draw(x,fa,-1);//保留重子树的数据 }
复杂度$O(nlogn)$
#include<cstdio> #include<vector> using namespace std; void read(int &x){ char c=getchar();x=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+c-48,c=getchar(); } #define N 1000005 int n,siz[N],big[N],cr[N],c[N],mx; long long ans[N],sum; bool vis[N]; vector <int> g[N]; void dfs1(int x,int fa){ siz[x]=1; for(auto i:g[x]) if(i!=fa){ dfs1(i,x); siz[x]+=siz[i]; if(siz[i]>siz[big[x]]) big[x]=i; } } void draw(int x,int fa,int k){ c[cr[x]]+=k; if(k>0&&c[cr[x]]>=mx){ if(c[cr[x]]>mx) sum=0; mx=c[cr[x]]; sum+=cr[x]; } for(auto i:g[x]) if(i!=fa&&!vis[i]) draw(i,x,k); } void dfs2(int x,int fa,bool is){ for(auto i:g[x]) if(i!=fa&&i!=big[x]) dfs2(i,x,0); if(big[x]) dfs2(big[x],x,1),vis[big[x]]=1; draw(x,fa,1); ans[x]=sum; if(big[x]) vis[big[x]]=0; if(!is) draw(x,fa,-1),sum=mx=0; } int main(){ read(n); for(int i=1;i<=n;++i) read(cr[i]); for(int i=1,p,q;i<n;++i){ read(p); read(q); g[p].push_back(q); g[q].push_back(p); }dfs1(1,0); dfs2(1,0,1); for(int i=1;i<=n;++i) printf("%lld ",ans[i]); return 0; }