codeforces 442D - Adam and Tree

題目大意:

給一棵樹上的每條邊染色,每種顏色只能用一次,一次染色樹上的一條鏈。

問(從根到所有節點經過的顏色的最大值)的最小值是多少。

樹是通過每次加一個點得到的,問每次加點之後的最小值是多少。

點數<=1,000,000

不難發現,答案肯定是小於等於直接樹剖的答案,也就是log當前點數。

顯然能想到dp:用f[i]表示處理i的所有子樹和i所需的最小顏色。如果正常染色的話,我們會嘗試把f值最小的兩棵子樹和i染成到i的邊相同的顏色,所以我們處理出m1[],m2[]表示每個節點子樹f值的最大值和第二大值。那麼如果m1[i]>m2[i]+1,我們就向m1[i]連邊,則f[i]等於m1[i]。否則,我們把m1[i]和m2[i]鏈接起來,f[i]=m2[i]+1(因爲此時i無法和fa[i]連接起來,答案就要+1)。

如果不考慮複雜度,那麼我們直接向上暴力修改的話,每次的複雜度是O(H),H是樹高。但是注意到答案相當小,也就是說N次修改,只有logN次會走到根,其他的都在半路影響就消除了。所以我們直接往上爬,如果當前點的f[i]值等於max(m2[i]+1,m1[i])就可以退出了。這是因爲正常情況下f[i]=max(m2[i]+1,m1[i]),如果相等的話,就不需要修改下去了。

附代碼:

#include<bits/stdc++.h>
#define N 1001000
using namespace std;
int n;
int f[N],fa[N],m1[N],m2[N];
int main(){
	scanf("%d",&n);
	for(int i=2,a;i<=n+1;i++){
		scanf("%d",&fa[i]);
		f[i]=1;
		int t=i;
		while(t!=1){
			if(f[t]>m1[fa[t]]) m2[fa[t]]=m1[fa[t]],m1[fa[t]]=f[t];
			else m2[fa[t]]=max(m2[fa[t]],f[t]);
			if(max(m2[fa[t]]+1,m1[fa[t]])==f[fa[t]]) break;
			else f[fa[t]]=max(m2[fa[t]]+1,m1[fa[t]]);
			t=fa[t];
		}
		printf("%d ",m1[1]);
	}
	puts("");
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章