1. 程式人生 > >[CF600E] Lomsat Gelral [樹鏈剖分]

[CF600E] Lomsat Gelral [樹鏈剖分]

題意: 給出一個有NN個點,以11號點為根的有根樹。每個點有一種顏色ciNc_i\le N。 以某個點為根的子樹中,如果一種顏色出現的次數不比其它顏色少,稱它是這個點的支配顏色。 點的支配顏色的和,是指,某個點的所有支配顏色的編號的和。 求這棵樹上每個點的支配顏色的和。 N105N\le10^5。

簡單地考慮:

可以暴力統計每個點,每種顏色的出現次數。Θ(N2)\Theta(N^2)

這種做法顯然會爆炸,也顯然有很大的優化空間。

我們知道到某個點的AnsAns等於它的所有兒子的AnsAns的和,所以兒子的資訊不能白白浪費。

dfsdfs

,然後返回的時候把子樹的貢獻加到父親上? 雖然好像少了刪除的步驟,但是實際上, 不僅爆了空間(存每個點的答案一共Θ(N2)\Theta(N^2)),也爆了時間(掃一遍加貢獻一共Θ(N2)\Theta(N^2))。

既然沒法完全儲存兒子的資訊,再加上只能儲存Θ(N)\Theta(N)級別的資訊(每個顏色出現次數)

那麼頂多就把一個兒子的資訊帶上來,其它兒子只能暴力了。顯然帶上重兒子最優。

這樣能夠減少多少開銷?

DFSDFS。訪問某一個點的時候, 我們先把它的輕兒子都逐個遞迴下去solvesolve,然後再解決重兒子。 統計完一個輕兒子要把它的資訊拋棄掉,再統計下一個 而統計完重兒子,這個點的所有兒子也都有答案了。 可以留下重兒子的資訊,再暴力加上輕兒子的資訊和正在訪問的點本身的資訊,得出這個點的答案。

怎麼分析複雜度?很明顯我們需要知道的就是某一個點會被暴力統計多少次

已經有了輕重兒子的概念了,乾脆按照樹鏈剖分的思路來分析 輕邊(u,v)(u,v)siz[v]siz[u]2siz[v]\le \frac{siz[u]}{2}, 並且由此可得一條從根向下的路徑經過的輕邊不會超過log2Nlog_2N條, 經過重鏈數量也不會超過輕邊數量+1+1

某個點到根的路徑上有多少條輕邊,這個點就會被統計幾次。

複雜度是Θ(NlogN)\Theta(NlogN)

也可以搞樹上啟發式合併,每次把小的合併進大的裡面。

ps.

ps.暴力統計可以在dfsdfs序上也可以直接在樹上暴力統計,隨意了

注意AnsAns可能爆intint

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cctype>
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;
}