A Aqours

给出的点可以视为是按照 BFS 序给的,也就是说从浅到深给出。可以再给每个节点 u 维护一个 fu 值, 表示离 u 最近的叶子节点到它的距离。 所以每当扫到一个叶子节点,就可以暴力往根节点跳,边跳边更新 f 值,直到跳到一个已被其他叶子节 点跳到过的节点为止。 那么对于当前的叶子节点,离它最近的编号小于它的叶子节点到它的距离就是跳到这个终止节点的 f 值 + 跳的步数。 在求完之后,还要从上述的终止节点沿着原路更新一下 f 值,因为可能当前叶子比较深但之前有比较浅 的叶子节点。 因为点是按从浅到深的顺序给出,所以一个节点的 f 值只会被最先跳到它的叶子节点赋值一次,也只会 被更新一次,所以复杂度是 O(n) 的。
至于为什么要原路返回更新一下 f 值。可以看一下下面的图。
ccpc wannafly winter camp day8
我们可以看一下6号点,如果从13更新 6 号点的话,f[6] 就是4, 其实 可以从 6 号点走到5号点,那么 f[6] 就是 2。
所以我们从叶子节点想上赋值的时候,还需要按照原路更新一下最小值。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6+1000;
int f[N],g[N],n,m,d[N];

int main(){
	scanf("%d",&n);

	for (int i = 2; i <= n; ++i){
		scanf("%d",&m);
		f[i] = m;
		d[m]++;
	}

	for (int i = 1; i <= n; ++i)
		if (!d[i]){  //找到叶节点
			printf("%d ",i);
			int x = i,y=1;
			while(f[x] && g[f[x]] == 0){  //每个节点到叶节点的距离、
				g[f[x]] = g[x] + 1;
				y++;
				x = f[x];
			}
			if (f[x] == 0) printf("-1\n"); else{
				printf("%d\n",y+g[f[x]]);
				g[f[x]] = min(g[f[x]],y);
				int cur = y + g[f[x]]; 
				int node = f[x]; x = i;
				while(x != node){  //更新当前节点到叶节点的距离、
					g[x] = min(g[x],cur--);
					x = f[x];
				}
			}
		}
	return 0;
}

相关文章: