jzoj3555树的直径

Posted SSL_ZZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jzoj3555树的直径相关的知识,希望对你有一定的参考价值。

题面

Description

科学家在观测一棵大树,这棵树在不断地生长,科学家给这棵树的每个节点编了号。开始的时候,这棵树很小只有4个节点,一号点为根,其他三个节点挂在上面。
在接下来的M次观察中,科学家每次都能看见这棵树从叶子处长出新的两个节点来。如果当前这棵树有N个节点,那么这棵树的新的两个节点的编号分别为N+1,N+2。科学家记录下了这棵树生长的过程,需要你帮着计算这棵树实时的直径。树的直径就是这棵树最远的两个节点的距离。

Input

第一行一个整数M,代表观察的次数。
接下来M行,每行一个整数x,代表这棵树的编号为x的节点下面又长了两个叶子节点。保证每次生长的节点都是叶子节点。

Output

M行,每次生长后这棵树的直径。

Sample Input

5
2
3
4
8
5

Sample Output

3
4
4
5
6

Data Constraint

对于10%的数据,N<=10
对于40%的数据,N<=1000
对于100%的数据,N<=100000。


解题思路

就是很J的一个东西

dep[i]记录以i为根的子树,i节点与最深节点的距离
那么对于以i为根的子树来说,直径 = 左子树最深距离 + 右子树最深 + 2
这个直径并不需要记录,每次可以直接套公式算出来
ans = max(ans, dep[son[i][1]] + dep[son[i][2]] + 2)
在这里插入图片描述
更新dep[]和ans
每次dep[]的更新只跟 当前新加的点 到 根节点 之间的dep[]有关
在这里插入图片描述
所以更新dep[]时就从新节点往父节点爬,ans也同时更新

当dep[]已经无法更新时,就可以直接退出了
在这里插入图片描述

这时dep[i]为2,无法再变大,dep不变大,ans也不会变大,那么就可以退出了
注意:要先更新ans,再判断退不退出
比如上面的图,左边的dep[son[i][1]]原本1,现在是2
现在 以i为根节点的子树 直径为4,可能更新ans,但dep[i]还是2,不会影响到上面的dep和ans


Code

#include <iostream>
#include <cstdio>

using namespace std;

int n, m, x, fa[500100], son[500100][3], ans, dep[500100];

void init() {
	dep[1] = 1; 
	fa[2] = fa[3] = fa[4] = 1;
	ans = 2;
	n = 4;
}

void change(int x) {  //加入新节点
	fa[n + 1] = fa[n + 2] = x;
	son[x][1] = n + 1, son[x][2] = n + 2;
	n += 2;
}

void work(int x) {
	while(fa[x] != 1) {
		int k = son[fa[x]][1];
		if(x == son[fa[x]][1]) k = son[fa[x]][2];  //找兄弟
		ans = max(ans, dep[x] + dep[k] + 2);
		
		if(dep[fa[x]] >= dep[x] + 1)  //无法更新dep
			break;
		dep[fa[x]] = dep[x] + 1;
		x = fa[x];
	}
	if(x == 2){  //因为比较懒, 所以写的很丑,可以专门写个函数用来查找x的兄弟
		int k = max(dep[3], dep[4]);
		ans = max(ans, dep[x] + k + 2);
		dep[fa[x]] = max(dep[fa[x]], dep[x] + 1);
	} else if(x == 3){
		int k = max(dep[2], dep[4]);
		ans = max(ans, dep[x] + k + 2);
		dep[fa[x]] = max(dep[fa[x]], dep[x] + 1);
	} else if(x == 4){
		int k = max(dep[2], dep[3]);
		ans = max(ans, dep[x] + k + 2);
		dep[fa[x]] = max(dep[fa[x]], dep[x] + 1);
	}
}

int main() {
	init(); 
	scanf("%d", &m);
	for(int i = 1; i <= m; i++) {
		scanf("%d", &x);
		change(x);
		work(n);
		printf("%d\\n", ans);
	}
} 

以上是关于jzoj3555树的直径的主要内容,如果未能解决你的问题,请参考以下文章

[JZOJ5465]道路重建--边双缩点+树的直径

bzoj 2785 jzoj 2755 2012东莞市选树的计数(计数+dp):

SDOI2013 直径(树的直径必经边)

找学生——(树的直径)

二叉树的直径-leetocde543

树的直径