Educational Codeforces Round 2 E. Lomsat gelral(树上启发式合并dsu on tree)

题目链接:https://codeforces.com/contest/600/problem/E

题目大意:

  求每一棵子树各自的出现最多次的颜色编号的和, n ≤ 1 e 5 n≤1e5 n1e5

题目思路:

  解决的最简单方法自然是直接暴力,但是可以发现对于1e5的范围无能为力。所以这里要引入一种新的算法,针对不带修改的子树问题有着独特的优势。
  dsu是并查集的意思,但是这个算法跟并查集没有一点关系,唯一有点搭边的是并查集的一种优化方法,就是每次都让小的部分并入大的来缩短时间。
  首先照搬树链剖分的概念,重儿子就是最大子树的根节点,dsu的思想就是,先暴力算所有轻儿子的情况,每个轻儿子算完都得清空自己的贡献,因为前一个孩子的计数会影响后面的孩子,算完后最后计算重孩子的情况。因为重孩子已经是最后一个处理的孩子了,剩下来的就是整个子树的大家族,也就是自己本身,加上重孩子和所有轻孩子的情况,所以重孩子是需要的一部分,不需要再删掉,然后暴力再把轻孩子和自己算上即可。时间复杂度的节省就在少算了一次重孩子上,根据大佬的复杂度推算,是 O ( n l o g n ) O(nlogn) O(nlogn)的。
  代码中cnt记录当前统计情况,dfs1处理每个点的重儿子,dfs2正式计算。flag是指是否删除当前贡献,1是删除,0不删除。首先先计算轻儿子,所以遇到重儿子跳过。这时候算的轻儿子需要去掉贡献。然后如果有重儿子的话就处理重儿子,重儿子的情况保留,同时更新Son,跟后面用来更新的add说一声现在的重儿子是Son,遇到它不用算了已经算过了。然后后面add的最后一个参数就是val,就是加或者删。加完孩子后就得到了当前点代表的子树的情况。Son变成0,因为如果要删当前的情况的话,得无差别全部删光,所以得把Son变成不会遍历到的点,才能保证全部删掉,否则重儿子的情况就被保留了。

以下是代码:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
const ll MAXN =2e5+5;
const ll MAXM = 4e7+5;
const ll MOD = 998244353;
int n,c[MAXN],siz[MAXN],son[MAXN],cnt[MAXN],x,y;
ll ans[MAXN];
vector<int>v[MAXN];
void dfs1(int u,int fa){
    
    
    siz[u]=1,son[u]=0;
    int len=v[u].size();
    rep(i,0,len-1){
    
    
        int y=v[u][i];
        if(y==fa)continue;
        dfs1(y,u);
        siz[u]+=siz[y];
        if(siz[y]>siz[son[u]]){
    
    
            son[u]=y;
        }
    }
}
int Son,maxx;
ll sum;
void add(int u,int fa,int val){
    
    
    cnt[c[u]]+=val;
    if(cnt[c[u]]>maxx){
    
    
        maxx=cnt[c[u]];
        sum=c[u];
    }
    else if(cnt[c[u]]==maxx){
    
    
        sum+=c[u];
    }
    int len=v[u].size();
    rep(i,0,len-1){
    
    
        int y=v[u][i];
        if(y==fa||y==Son)continue;
        add(y,u,val);
    }
}
void dfs2(int u,int fa,int flag){
    
    
    int len=v[u].size();
    rep(i,0,len-1){
    
    
        int y=v[u][i];
        if(y==fa||y==son[u])continue;
        dfs2(y,u,1);
    }
    if(son[u])dfs2(son[u],u,0),Son=son[u];
    add(u,fa,1);
    Son=0;
    ans[u]=sum;
    if(flag)add(u,fa,-1),sum=maxx=0;
}
int main()
{
    
    
    while(cin>>n){
    
    
        memset(cnt,0,sizeof(cnt));
        Son=maxx=sum=0;
        rep(i,1,n)cin>>c[i];
        rep(i,1,n)v[i].clear();
        rep(i,1,n-1){
    
    
            cin>>x>>y;
            v[x].push_back(y);
            v[y].push_back(x);
        }
        dfs1(1,0);
        dfs2(1,0,0);
        rep(i,1,n){
    
    
            cout<<ans[i]<<" ";
        }cout<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/toohandsomeIeaseId/article/details/104663653