题解:CF1225F Tree Factory

题目传送门

题意

给出一棵树,要求构造一条链,和一个操作序列,使得经过这些操作后,这条链可以变成给定的树。只有一种操作,把自己变成自己父亲的兄弟,即断开自己和父亲的连边,和父亲的父亲连边。

思路

考虑到正着做不好做,所谓正难则反,我考虑倒着做。那么题目就变成了,要求把给定的树变成一条链,每次的操作是把一条链与另一条链合并,贪心的发现,合并的代价是短的那条链的长度。基于这个性质,我们可以推出一个正确的贪心策略:对树进行长链剖分,每次都从子树下的最长链开始向其他链合并,代价就是除最长链之外其他链的长度总和。

如果手玩几次之后就会发现,最终的链上的节点的顺序对应着长链剖分后的 dfn 序。这样我们就可以轻松的解决第一个问题。现在考虑怎么输出操作序列,我们观察 dfn 序会发现,如果当前节点 dfn 序的父亲的深度和前一个节点的 dfn 的深度不相同,就说明这两个节点之间发生了操作,基于操作的性质,我们只需要计算这两个深度之间相差多少,即可知道操作了多少次,因为我们的贪心是从长链往短链合并,所以越靠后的点说明在原树中所处的链越长,那么每次操作我们都拿当前节点操作即可。

文字叙述不好理解,可以借助代码多看几次。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010101;
ll n,dep[N],head[N],tot,siz[N],f[N],dfn[N],son[N],ans,cnt;
struct edge{ll to,next;}e[N];
void add(ll u,ll v){
    e[++tot].to=v;
    e[tot].next=head[u];
    head[u]=tot;
}
void dfs(ll u,ll fa){
    f[u]=fa,dep[u]=dep[fa]+1;
    for(int i=head[u];i;i=e[i].next){
    	ll v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        siz[u]=max(siz[u],siz[v]+1);
        if(siz[v]+1>siz[son[u]])son[u]=v;
    }
}
void dfs1(ll u,ll fa){
    dfn[++cnt]=u;
    for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
        if(v==fa||v==son[u])continue;
        dfs1(v,u);
    }
    if(son[u])dfs1(son[u],u);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=2;i<=n;i++){
        ll x;
        cin>>x;x++;
        add(i,x);
        add(x,i);
    }
    dfs(1,0);
    dfs1(1,0);
    for(int i=1;i<=n;i++)cout<<dfn[i]-1<<" ";
    for(int i=2;i<=n;i++)ans+=dep[dfn[i-1]]-dep[f[dfn[i]]];
    cout<<"\n"<<ans<<"\n";
    for(int i=2;i<=n;i++){
        for(int j=dep[f[dfn[i]]];j<dep[dfn[i-1]];j++){
            cout<<dfn[i]-1<<" ";
        }
    }
    return 0;
}
posted @ 2025-09-07 21:39  一班的hoko  阅读(3)  评论(0)    收藏  举报