题意:一棵初始时为空的树,依次加入 n n n 个叶结点,每次加入后询问 用若干不同颜色的路径将树边染色后 每个点到根经过的颜色数 的最大值 的最小值。
n ≤ 1 0 6 n\leq 10^6 n≤106
首先发现这个路径没啥用,其实就是个剖分方案。
然后我考场以为是重链剖分,瞎胡了个东西上去获得了 5 分的好成绩。
把边的颜色放在儿子上,根结点没有颜色。这样所求就是到根路径上的所有点的颜色种类,根结点特判一下。
设 f ( u ) f(u) f(u) 表示 u u u 为根的答案,转移时选择除一个儿子外其他儿子 + 1 +1 +1 再取 max \max max ,显然是取 f f f 最大的,称为重儿子。
由于存在一个叫重链剖分的东西,所以这个 f ( u ) f(u) f(u) 是 O ( log s i z u ) O(\log siz_u) O(logsizu) 的。考虑暴力。
维护每个点的重儿子和 dp 值,插入一个叶子后暴力往上跳。如果是重儿子,直接让父亲取 max \max max。如果不是,判下是否需要切换成重儿子,如果切换的话父亲的 dp 值修改为原来的重儿子 + 1 +1 +1。更新不了后立刻退出。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 1000005
using namespace std;
inline int read()
{
int ans=0;
char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
int fa[MAXN],dp[MAXN],mx[MAXN];
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int n=read();
puts("0");
for (int u=2;u<=n;u++)
{
fa[u]=read();
dp[u]=1;
for (int i=u;i>1;i=fa[i])
if (fa[i]==1) dp[1]=max(dp[1],dp[i]);
else
if (i==mx[fa[i]])
{
if (dp[i]>dp[fa[i]]) ++dp[fa[i]];
else break;
}
else
{
if (!mx[fa[i]]) mx[fa[i]]=i;
else if (dp[i]==dp[fa[i]]) dp[fa[i]]=dp[mx[fa[i]]]+1,mx[fa[i]]=i;
else break;
}
printf("%d\n",dp[1]);
}
return 0;
}