当前位置: 代码迷 >> 综合 >> [cf600e] Lomsat gelral(树上启发式合并)
  详细解决方案

[cf600e] Lomsat gelral(树上启发式合并)

热度:88   发布时间:2024-02-09 01:14:56.0

题意是说,树上每个节点有一个颜色,如果在一颗子树中,某种颜色出现的次数不小于其他颜色,就说该颜色占领这颗子树,求每个子树的所有占领颜色之和。

树上启发式合并的板子题。cnt[i]表示当前颜色i出现的次数,mx为当前出现最多的颜色出现次数,sum[I]表示i子树的答案。首先想树链剖分一样求出每个节点的重儿子。当我们求节点u的子树答案时,先将所有非重儿子的答案求出,每次求完都将cnt清零。然后求重儿子的答案,cnt不清零,且加上当前节点的贡献。之后再遍历一遍非重儿子的子树,添加对cnt的贡献,遍历中求出sum。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=100010;
struct edge{int y,next;
}data[N*2];
int n,num,h[N],c[N],mx,siz[N],son[N],cnt[N];
ll sum[N];
inline int read(){int x=0,f=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return f?-x:x;
} 
inline void addedge(int x,int y){data[++num].y=y;data[num].next=h[x];h[x]=num;
}
void dfs1(int u,int fa){siz[u]=1;son[u]=0;for(int i=h[u];i!=-1;i=data[i].next){int v=data[i].y;if(v!=fa){dfs1(v,u);siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v;}}
}
void dfs3(int u,int fa,int x){cnt[c[u]]++;if(cnt[c[u]]>mx){sum[x]=1ll*c[u];mx=cnt[c[u]];}else if(cnt[c[u]]==mx)sum[x]+=1ll*c[u];for(int i=h[u];i!=-1;i=data[i].next){int v=data[i].y;if(v!=fa)dfs3(v,u,x);}
}
void dfs2(int u,int fa){memset(cnt,0,sizeof cnt);mx=0;for(int i=h[u];i!=-1;i=data[i].next){int v=data[i].y;if(v!=fa&&v!=son[u]){dfs2(v,u);mx=0;memset(cnt,0,sizeof cnt);}}if(son[u])dfs2(son[u],u);sum[u]=sum[son[u]];cnt[c[u]]++;if(cnt[c[u]]>mx){sum[u]=1ll*c[u];mx=cnt[c[u]];}else if(cnt[c[u]]==mx)sum[u]+=1ll*c[u];for(int i=h[u];i!=-1;i=data[i].next){int v=data[i].y;if(v!=fa&&v!=son[u])dfs3(v,u,u);}
}
int main(){n=read();for(int i=1;i<=n;i++)c[i]=read();memset(h,-1,sizeof h);num=0;for(int u,v,i=1;i<n;i++){u=read();v=read();addedge(u,v);addedge(v,u);}siz[0]=0;dfs1(1,0);dfs2(1,0);for(int i=1;i<=n;i++)printf("%I64d ",sum[i]);return 0;
}