E. Lomsat gelral(树上启发式合并)

看好多人说树上启发式合并是一种优美的暴力美学,他的时间复杂度是(nlogn),

它的主要思想就是小的向大的里边合并,从而减少时间 

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
typedef pair<pair<int,int>,int>PII;
vector<int>h[N];
int col[N],son[N],cnt[N];
map<int,int>mp;
int sum=0,s[N],ma,Son;
//cnt记载个数,son记载重儿子
void dfs(int a,int fa)
{
    cnt[a]=1;
    for(auto it:h[a])
    {
        if(it==fa)continue;
        dfs(it,a);
        cnt[a]+=cnt[it];
        if(cnt[it]>cnt[son[a]])son[a]=it;
    }
}
/*var==1统计答案数目函数,var==-1将轻儿子的数目减去,
 * 如果var==-1代表这个儿子是轻儿子,此时需要将这个儿子的贡献减去(包括该儿子中的重儿子)
 * 如果var==1代表统计这个儿子的贡献
 * 在统计某点答案时,只有该点下的重儿子不用统计
 * */
void update(int a,int fa,int var)
{
    mp[col[a]]+=var;
    if(mp[col[a]]>ma)ma=mp[col[a]],sum=col[a];
    else if(mp[col[a]]==ma)sum+=col[a];
    for(auto it:h[a])
    {
        if(it==fa||it==Son)continue;
        update(it,a,var);
    }
}
/*首先去完成轻儿子的答案统计,如果存在重儿子则最后统计重儿子
 * 该点完成后要判断这一点是父亲的轻儿子还是重儿子,如果是轻儿子则将贡献删掉,重儿子则不用
 * */
void dfs(int a,int fa,int pa)
{
    for(auto it:h[a])
    {
        if(it==fa||it==son[a])continue;
        dfs(it,a,0);
    }
    if(son[a])dfs(son[a],a,1);
    Son=son[a];
    update(a,fa,1);
    Son=0;
    s[a]=sum;
    if(!pa)update(a,fa,-1),sum=ma=0;
}
void sove()
{
   int n;
   cin>>n;
   for(int i=1;i<=n;i++)cin>>col[i];
   for(int i=1;i<n;i++)
   {
       int a,b;
       cin>>a>>b;
       h[a].push_back(b);
       h[b].push_back(a);
   }
   dfs(1,-1);
   dfs(1,-1,1);
   for(int i=1;i<=n;i++)cout<<s[i]<<" ";
}
signed main ()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t=1;
    // cin>>t;
    while(t--)sove();
}

例题

Problem - E - Codeforces

问题 - 208E - 代码强制 (codeforces.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值