题意:一棵初始时为空的树,依次加入 nnn 个叶结点,每次加入后询问 用若干不同颜色的路径将树边染色后 每个点到根经过的颜色数 的最大值 的最小值。
n≤106n\leq 10^6n≤106
首先发现这个路径没啥用,其实就是个剖分方案。
然后我考场以为是重链剖分,瞎胡了个东西上去获得了 5 分的好成绩。
把边的颜色放在儿子上,根结点没有颜色。这样所求就是到根路径上的所有点的颜色种类,根结点特判一下。
设 f(u)f(u)f(u) 表示 uuu 为根的答案,转移时选择除一个儿子外其他儿子 +1+1+1 再取 max\maxmax ,显然是取 fff 最大的,称为重儿子。
由于存在一个叫重链剖分的东西,所以这个 f(u)f(u)f(u) 是 O(logsizu)O(\log siz_u)O(logsizu) 的。考虑暴力。
维护每个点的重儿子和 dp 值,插入一个叶子后暴力往上跳。如果是重儿子,直接让父亲取 max\maxmax。如果不是,判下是否需要切换成重儿子,如果切换的话父亲的 dp 值修改为原来的重儿子 +1+1+1。更新不了后立刻退出。
复杂度 O(nlogn)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]);elseif (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;
}