题目大意:
给一棵树上的每条边染色,每种颜色只能用一次,一次染色树上的一条链。
问(从根到所有节点经过的颜色的最大值)的最小值是多少。
树是通过每次加一个点得到的,问每次加点之后的最小值是多少。
点数<=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;
}