看好多人说树上启发式合并是一种优美的暴力美学,他的时间复杂度是(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();
}
例题