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
#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;
}


你可能感兴趣的:(codeforces,乱搞,dp)