题意
给定一个
n
n
个节点的树,对于每个子树,输出子树中出现次数最多的节点编号之和。(次数最多的编号有多个节点都要统计进去)。
思路
无疑仍是树上 dsu d s u ,考虑到需要维护节点出现次数最多的编号和,所以实时维护一个数组 cnt c n t 保存每种编号出现的次数,然后再用两个变量分别保存最多出现次数和答案即可。由于树上 dsu d s u 有一个性质,每次 update(−1) u p d a t e ( − 1 ) 的时候都刚好把所有信息都清除,所以可以将上述的两个变量直接还原为 0 0 ,然后将 上减即可,不用维护其他信息。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int L[N],R[N],sz[N],son[N],ori[N],cnt[N],col[N],ord,maxocc,n;
LL res[N],sum;
void dfs(int u,int f)
{
L[u]=++ord,sz[u]=1,son[u]=0,ori[ord]=u;
EOR(i,G,u)
{
int v=G.to[i];
if(v==f)continue;
dfs(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
R[u]=ord;
}
void add(int L,int R,int val)
{
FOR(i,L,R)
{
cnt[col[ori[i]]]+=val;
if(cnt[col[ori[i]]]>maxocc)maxocc++,sum=col[ori[i]];
else if(cnt[col[ori[i]]]==maxocc)sum+=col[ori[i]];
}
}
void dsu(int u,int f)
{
EOR(i,G,u)
{
int v=G.to[i];
if(v==f||v==son[u])continue;
maxocc=sum=0;
dsu(v,u);
add(L[v],R[v],-1);
}
maxocc=sum=0;
if(son[u])dsu(son[u],u);
EOR(i,G,u)
{
int v=G.to[i];
if(v==f||v==son[u])continue;
add(L[v],R[v],1);
}
add(L[u],L[u],1);
res[u]=sum;
}
void clear()
{
G.clear();
memset(cnt,0,sizeof(cnt));
maxocc=sum=ord=0;
}
int main()
{
while(~scanf("%d",&n))
{
clear();
FOR(i,1,n)scanf("%d",&col[i]);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
G.add(u,v);
G.add(v,u);
}
dfs(1,0);
dsu(1,0);
FOR(i,1,n-1)printf("%lld ",res[i]);
printf("%lld\n",res[n]);
}
return 0;
}