CodeForces 600E Lomsat gelral

題目大意

%  一棵樹有 nn 個結點,每個結點上有一種顏色,每個顏色有一個編號,求樹中每個子樹的最多的顏色編號的和。
  數據範圍 1n1051\leqslant n\leqslant 10^5

題解

%  考慮dsu on tree的過程。

  1. 遞歸處理輕兒子。
  2. 遞歸處理重兒子。
  3. 暴力統計除了重兒子之外的子樹信息,加到桶中。
  4. 累加答案。
  5. 如果當前節點 uu 不是uu 的父節點的重兒子,刪除這顆子樹(包括uu 的重兒子)對桶的所有貢獻,否則保留信息在桶中,直接返回。

%  這個過程對於一個已經明白算法運行邏輯的人來說已經很清楚了,但是對不不明白的人來說,仍然比較混亂,而且似乎找不到什麼好的措辭來解釋這部分內容,因此最好的辦法是結合代碼使用,下面的代碼儘量寫得每一行關鍵代碼都有註釋。
  理解後我們來考慮這個過程的時間複雜度,對於一個點 uu,若其到根節點的路徑上有 kk 條輕邊,則這個點會被計算 kk 次,由樹鏈剖分的知識,我們可以得到,對於任意點 uu,恆有 klog2nk\leqslant \log_2 n,因此總時間複雜度不超過 T(n)=Θ(nlog2n)\text T(n)=\Theta(n\log_2 n)

#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
struct edge{
	int v,next;
}edges[maxn<<1];
int n,head[maxn];
void ins(int u,int v){
	static int cnt=0;
	edges[++cnt]=(edge){v,head[u]};
	head[u]=cnt;
}
int size[maxn],son[maxn];
void dfs(int u,int fa=-1){
	size[u]=1;
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(v==fa) continue;
		dfs(v,u); size[u]+=size[v];
		if(size[son[u]]<size[v]) son[u]=v;
	}
}
int c[maxn];//節點的亞瑟 
int cnt[maxn];//顏色的出現次數,即桶 
int max_cnt;//出現次數最多的顏色的出現次數 
int stop;//禁足點,用於標記重兒子 
long long sum;//出現次數最多的顏色的編號和 
void calc(int u,int fa,int val){
	cnt[c[u]]+=val;//累加到桶重 
	if(max_cnt<cnt[c[u]]) max_cnt=cnt[c[u]],sum=c[u];//找到了出現次數更多的顏色,更新答案 
	else if(max_cnt==cnt[c[u]]) sum+=c[u];//又出現了一種出現次數一樣多的顏色,累加答案 
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(v==fa||v==stop) continue;//忽略禁足點和父節點 
		calc(v,u,val);//暴力遞歸統計答案 
	}
}
long long ans[maxn];
void work(int u,int fa,int chain){
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(v==fa||v==son[u]) continue;//先忽略中兒子和父親 
		work(v,u,0);//遞歸處理輕兒子 
	}
	if(son[u]){//考慮中兒子 
		work(son[u],u,1);//遞歸處理重兒子 
		stop=son[u];//標記重兒子不能在下面的calc函數中計算,因爲在上一行的遞歸中,沒有刪除重兒子桶的貢獻,已經被統計過了。 
	}
	calc(u,fa,1);//暴力統計所有輕兒子的答案 
	stop=0;//這裏刪除對重兒子的禁足是爲了下面50行的calc函數能把包括重兒子的整顆子樹都刪掉。 
	ans[u]=sum;//存下這個節點的答案——顏色編號和。 
	if(chain==0){//如果這是一條重鏈的開頭,意味着其父親和它的連邊爲輕邊,要刪除貢獻 
		calc(u,fa,-1);//刪除整顆子樹的貢獻 
		sum=max_cnt=0;//清空統計的信息 
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&c[i]);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		ins(u,v);ins(v,u);
	} dfs(1); work(1,-1,1);
	for(int i=1;i<=n;i++)
		printf("%I64d ",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章