題目大意:
給一棵樹上的每條邊染色,每種顏色只能用一次,一次染色樹上的一條鏈。
問(從根到所有節點經過的顏色的最大值)的最小值是多少。
樹是通過每次加一個點得到的,問每次加點之後的最小值是多少。
點數<=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;
}