题意
一条链,每次可以选择一个节点 u u u使得其 f a [ f a [ u ] ] fa[fa[u]] fa[fa[u]]变成 f a [ u ] fa[u] fa[u]
其中 u u u和 f a [ u ] fa[u] fa[u]都不是根节点,并且操作后其他节点父亲节点都不变
你可以通过若干次这样的操作使得这一条链变成一棵树
现在给你一棵 n n n个节点的树,求能用最少的操作变出这棵树的链
2 ≤ n ≤ 1 0 5 , 0 ≤ 2\le n\le10^5,0\le 2≤n≤105,0≤操作数 k ≤ 1 0 6 k\le10^6 k≤106
题解
考虑一次对
w
w
w的操作
那么把树变回链的逆操作就是当前节点变成兄弟节点的父亲
考虑合并一个节点的两个子节点的子链的代价
由于每次都只能将一个点并到链上,那么把一条子链并到另外一条子链上的代价就是链长
形象一点就是这样
所以可以考虑贪心每次把短链合并到长链上
可以发现把短链并到长链上后长链上的点一定是最终链上最后的那一部分点
由于短链去合并的顺序并不影响答案,所以最终的链其实就是整棵树的 d f n dfn dfn,其中长链节点是最后一步 d f s dfs dfs的
时间复杂度 O ( n + k ) O(n+k) O(n+k)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
using arr=int[N];
int n,m,Ans;
arr f,fa,dep,dfn,Son;
vector<int>G[N];
void dfs(int u){
dfn[++m]=u;
for(auto v:G[u])
if(v!=Son[u])
dfs(v);
if(Son[u])dfs(Son[u]);
}
int main(){
scanf("%d",&n);
for(int i=2,u;i<=n;++i){
scanf("%d",&u);
fa[i]=++u;
G[u].push_back(i);
dep[i]=dep[u]+1;
}
for(int i=n;i>1;--i)
if(f[i]+1>=f[fa[i]]){
f[fa[i]]=f[i]+1;
Son[fa[i]]=i;
}
dfs(1);
for(int i=1;i<=n;++i)printf("%d%c",dfn[i]-1," \n"[i==n]);
for(int i=2;i<=n;++i)Ans+=dep[dfn[i-1]]-dep[fa[dfn[i]]];
printf("%d\n",Ans);
for(int i=2;i<=n;++i)
for(int j=dep[dfn[i-1]]-dep[fa[dfn[i]]];j;--j)
printf("%d ",dfn[i]-1);
return 0;
}