你是一个程序猿,现在有一棵新年树(并不是传统的带着叶子的树)——它有四个节点: , , , . 其中 , , 的父亲都是 .
新年里,程序猿们往往会做一些有趣的事情。你则选择以往这棵树上加节点来取乐。 一个添加节点的操作是这样的:
- 找到树上的一个叶子结点 .
- 设现在树上有 个节点,那么你现在会加入两个节点 和 ,它们都会成为 的儿子.
你的任务是在做 次这样的操作,并在每做完一次后计算一次树的直径。来吧,我们一起来解决这道新年问题吧!
链接🔗
Codeforces
伪题解🔗
我们考虑树形dp计算树的直径的过程。
我们如果令 为以 为根的子树中最长的链的长度, 为以 为根的子树中的直径长度(过根节点),那么就有如下转移:
如果我们令 为 的深度,那么我们可以将第一个改写如下:
我们可以用倍增在 的时间内找到第一个不需要更新的位置,然后在倍增上用 的时间内更新 值,计算得到 (每次深度只增加1,所以一定会只有一个delta),然后将 修改维护即可。
看了 Tutorial 之后有些自闭,题解给出了一个非常轻松愉悦的办法。
题解🔗
我们思考 计算直径的过程,从一个节点找到最远的一个节点,这个节点一定是直径的一个端点,然后我们再进行一遍 最远点就是直径的另一个的端点。
所以我们考虑从根节点进行第一次 ,找到最远的第一个节点(事实上是深度最大的节点之一皆可)。
如果我们给一个节点新建了两个子节点,我们发现它们的父亲是当前最远的节点,那么答案一定增加且只增加了 ;
否则,答案只有可能被当前最远的节点和新增加的节点之间的距离更新。
那么就可以倍增维护 计算树上距离,在 的时间内解决这个问题。
代码🔗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| #include <bits/stdc++.h>
using namespace std;
const int MAXN = 1001000,logn = 22;
int q;
int f[MAXN][logn],dep[MAXN],t[MAXN];
int lca(int x,int y){
if(dep[x] < dep[y]) swap(x,y);
for(int i = logn-1;i>=0;--i){
if(dep[f[x][i]] >= dep[y]) x = f[x][i];
}
if(x == y) return x;
for(int i = logn-1;i>=0;--i){
if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i];
}
return f[x][0];
}
void addnode(int x,int fa){
f[x][0] = fa,dep[x] = dep[fa]+1;
for(int i = 1;i<logn;i++){
f[x][i] = f[f[x][i-1]][i-1];
}
}
int caldis(int x,int y){
return dep[x] + dep[y] - 2 * dep[lca(x,y)];
}
void init(){
scanf("%d",&q);
for(int i = 1;i<=q;i++)
scanf("%d",&t[i]);
}
void solve(){
dep[1] = 1,dep[2] = dep[3] = dep[4] = 2;
f[2][0] = f[3][0] = f[4][0] = 1;
int maxdep = 2,maxnode = 2,ans = 2,n = 4;
for(int i = 1;i<=q;i++){
int x = t[i];
addnode(++n,x),addnode(++n,x);
if(dep[x] == maxdep){
maxdep++,maxnode = n,ans++;
}
else{
ans = max(ans,caldis(maxnode,n));
}
printf("%d\n",ans);
}
}
int main(){
init();
solve();
return 0;
}
|