Description
传送门
- 一个爷爷非空的节点x可以做一个操作:fa(x)=fa(fa(x))
- 给一棵树,求如何将一条链通过最少的操作次数变成这个树,节点编号要完全一致。
- 输出这条链初始的编号,以及操作次数,和每一次操作的节点编号。
- n<=1e5
Solution
- 构造题。
- 思路比较神奇。首先反过来,求树变成链。
- 首先考虑最少的操作次数。从树的深度切入。
- 最初的深度是dep,最终的深度是n,每一次最多让深度加一。
- 那么答案的下限就是n-dep。
- 能不能有一种方案达到这个下限呢?实际上是有的。
- 考虑对于最长链,找到上面的最深的分叉点,在这里断开。
- 将最长链所在的儿子接到任意一个其他的儿子上,那么深度就一定会增加1。
- 可以证明如果没有分叉点的话一定已经是一条链了。
- 这样O(n)构造一波就好了。从最深点往上遍历各个子树。
- 这种构造题先找答案下限再去满足的套路要熟悉运用。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;
int n,m,i,j,k,mxdep,dep[maxn],fa[maxn],cnt[maxn];
int x,d[maxn],vis[maxn],tot,ans[maxn];
int em,e[maxn],nx[maxn],ls[maxn];
void insert(int x,int y){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
cnt[x]++;
}
int main(){
scanf("%d",&n);
dep[0]=1;
for(i=1;i<n;i++) {
scanf("%d",&fa[i]),dep[i]=dep[fa[i]]+1;
x=(dep[i]>dep[x])?i:x;
insert(fa[i],i);
}
mxdep=dep[x];
while (cnt[x]==0&&x) d[++d[0]]=x,vis[x]=1,cnt[fa[x]]--,x=fa[x];
for(tot=1;tot<=n-mxdep;tot++){
for(int &i=ls[x];i;i=nx[i]) if (!vis[e[i]]){
x=e[i],ans[tot]=d[d[0]];
break;
}
while (cnt[x]==0&&x) d[++d[0]]=x,vis[x]=1,cnt[fa[x]]--,x=fa[x];
}
printf("0 ");
while (d[0]) printf("%d ",d[d[0]--]);
printf("\n%d\n",n-mxdep);
for(i=n-mxdep;i>=1;i--) printf("%d ",ans[i]);
}