题意:
给出一个有 N N N个点,以 1 1 1号点为根的有根树。每个点有一种颜色 c i ≤ N c_i\le N ci≤N。
以某个点为根的子树中,如果一种颜色出现的次数不比其它颜色少,称它是这个点的支配颜色。
点的支配颜色的和,是指,某个点的所有支配颜色的编号的和。
求这棵树上每个点的支配颜色的和。
N ≤ 1 0 5 。 N\le10^5。 N≤105。
简单地考虑:
可以暴力统计每个点,每种颜色的出现次数。 Θ ( N 2 ) \Theta(N^2) Θ(N2)
这种做法显然会爆炸,也显然有很大的优化空间。
我们知道到某个点的 A n s Ans Ans等于它的所有儿子的 A n s Ans Ans的和,所以儿子的信息不能白白浪费。
d f s dfs dfs,然后返回的时候把子树的贡献加到父亲上?
虽然好像少了删除的步骤,但是实际上,
不仅爆了空间(存每个点的答案一共 Θ ( N 2 ) \Theta(N^2) Θ(N2)),也爆了时间(扫一遍加贡献一共 Θ ( N 2 ) \Theta(N^2) Θ(N2))。
既然没法完全保存儿子的信息,再加上只能存储 Θ ( N ) \Theta(N) Θ(N)级别的信息(每个颜色出现次数)
那么顶多就把一个儿子的信息带上来,其它儿子只能暴力了。显然带上重儿子最优。
这样能够减少多少开销?
D F S DFS DFS。访问某一个点的时候,
我们先把它的轻儿子都逐个递归下去 s o l v e solve solve,然后再解决重儿子。
统计完一个轻儿子要把它的信息抛弃掉,再统计下一个
而统计完重儿子,这个点的所有儿子也都有答案了。
可以留下重儿子的信息,再暴力加上轻儿子的信息和正在访问的点本身的信息,得出这个点的答案。
怎么分析复杂度?很明显我们需要知道的就是某一个点会被暴力统计多少次
已经有了轻重儿子的概念了,干脆按照树链剖分的思路来分析
轻边 ( u , v ) (u,v) (u,v)有 s i z [ v ] ≤ s i z [ u ] 2 siz[v]\le \frac{siz[u]}{2} siz[v]≤2siz[u],
并且由此可得一条从根向下的路径经过的轻边不会超过 l o g 2 N log_2N log2N条,
经过重链数量也不会超过轻边数量 + 1 +1 +1。
某个点到根的路径上有多少条轻边,这个点就会被统计几次。
复杂度是 Θ ( N l o g N ) \Theta(NlogN) Θ(NlogN)。
p s . ps. ps.暴力统计可以在 d f s dfs dfs序上也可以直接在树上暴力统计,随意了
注意 A n s Ans Ans可能爆 i n t int int
本题也可以搞树上启发式合并,每次把小的合并进大的里面;还可以用 m a p map map暴力。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define add_edge(a,b) nxt[++tot]=head[a],head[a]=tot,to[tot]=b
int N,tot=0,mx=0;
LL tmp=0;
int ci[100005]={},nxt[200005]={},head[200005]={},to[200005]={};
int siz[100005]={},son[100005]={};
int cnt[100005]={};
LL ans[100005]={};
bool vis[100005]={};
void pthDec(int x,int fa)
{
siz[x]=1;
for(int t=1,i=head[x];i;i=nxt[i])
{
if(to[i]==fa)continue;
pthDec(to[i],x); siz[x]+=siz[to[i]];
if(siz[to[i]]>t)t=siz[to[i]],son[x]=to[i];
}
}
void modify(int x,int fa,int delta)
{
cnt[ci[x]]+=delta;
if((delta==1)&&(cnt[ci[x]]>=mx))
{
if(cnt[ci[x]]>mx)tmp=0,mx=cnt[ci[x]];
tmp+=ci[x];
}
for(int i=head[x];i;i=nxt[i])
{
if((to[i]==fa)||vis[to[i]])continue;
modify(to[i],x,delta);
}
}
void dfs(int x,int fa,bool keep)
{
for(int i=head[x];i;i=nxt[i])
{
if(to[i]==fa||to[i]==son[x])continue;
dfs(to[i],x,0);
}
if(son[x])dfs(son[x],x,1),vis[son[x]]=1;
modify(x,fa,1),ans[x]=tmp;
if(son[x])vis[son[x]]=0;
if(!keep)modify(x,fa,-1),mx=tmp=0;
}
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)scanf("%d",&ci[i]);
for(int u,v,i=1;i<N;++i)
{
scanf("%d%d",&u,&v);
add_edge(u,v); add_edge(v,u);
}
pthDec(1,0);
dfs(1,0,0);
for(int i=1;i<=N;++i)printf("%I64d ",ans[i]);
return 0;
}