CF600E Lomsat gelral(dsu on tree)

时间:2022-12-31 13:10:21

Lomsat gelral

题目描述

一个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入门题

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;
}