dfs序
欧拉序
拓扑序
树上启发式合并板子(E - Lomsat gelral)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PII;
const int N=1e5+5;
const int M=2e5+10;
int col[N],cnt[N];
int e[M],ne[M],h[M],idx;
int siz[N],son[N];
ll ans[N],sum;//ans是答案数组,sum用于累加计算出当前子树的答案
int flag,maxc;//flag用于标记重儿子,maxc用于更新最大值
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int f){//树链剖分找重儿子
siz[u]=1;
for(int i=h[u];i!=-1;i=ne[i]){
int v=e[i];
if(v==f){
continue;
}
dfs(v,u);//u是父节点,v是子节点
siz[u]+=siz[v];//记录子节点的个数
if(siz[v]>siz[son[u]])son[u]=v;//记录重儿子
}
}
void countt(int u,int f,int val){
cnt[col[u]]+=val;//增加还是删除贡献
if(cnt[col[u]]>maxc){
maxc=cnt[col[u]];
sum=col[u];
}
else if(cnt[col[u]]==maxc){//颜色数相等
sum+=col[u];
}
for(int i=h[u];i!=-1;i=ne[i]){
int v=e[i];
if(v==f||v==flag)continue;
countt(v,u,val);
}
}
void dfs(int u,int f,bool keep){//dsu on tree板子
//*第一步,搞轻儿子及其子树算答案删贡献
for(int i=h[u];i!=-1;i=ne[i]){
int v=e[i];
if(v==f||v==son[u])continue;//只往轻儿子方向寻找
dfs(v,u,false);
}
//*第二步,搞重儿子及其子树算其答案不删贡献
if(son[u]){
dfs(son[u],u,true);
flag=son[u];
}
//*第三步,暴力统计u及其所有轻儿子的贡献到刚算出的重儿子信息里
countt(u,f,1);
flag=0;
ans[u]=sum;
//*把需要删除贡献的删除
if(!keep){
countt(u,f,-1);
sum=maxc=0;
}
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
//int t;
//cin>>t;
//t=1;
//while (t--)
//{
//solve();
//}
memset(h,-1,sizeof h);
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;
add(a,b);
add(b,a);
}
dfs(1,0);
dfs(1,0,0);
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}