CF911F Tree Destruction

题目链接:https://www.luogu.com.cn/problem/CF911F

solution:先求得树的直径,再求得在树的直径上的节点和不在树的直径上的节点。我们考虑优先删除不在直径上的节点,这样不会破坏树的直径,在删完了这些点之后再慢慢删直径上的点。


#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+10;
int n;
vector<int> vec[maxn];
int u,v,dis[maxn],on[maxn],fa[maxn];
void dfs1(int u)
{
    for(auto v:vec[u])
    {
        if(dis[v]==-1)
        {
            dis[v]=dis[u]+1;
            dfs1(v);
        }
    }
}
void dfs2(int now)
{
    for(auto to:vec[now])
    {
        if(dis[to]>dis[now])
        {
            fa[to]=now;
            dfs2(to);
            on[now]=on[now]||on[to];//处理在直径上的点
        }
    }
}
int ans=0;
vector<array<int,3> >s;
void dfs3(int now,int rt)
{
    for(auto to:vec[now])
    {
        if(dis[to]>dis[now])
        {
            dfs3(to,on[to]?to:rt);
            //可以理解为直径是纵向的道路,不在直径上的是横向的道路
        }
    }
    if(!on[now])
    {
        if(dis[now]>dis[now]+dis[v]-(dis[rt]<<1))
        {
            ans+=dis[now];
            s.push_back({u,now});
        }
        else 
        {
            ans+=dis[now]+dis[v]-(dis[rt]<<1);
            s.push_back({v,now});
        }
    }
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        scanf("%lld%lld",&u,&v);
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    memset(dis,-1,sizeof(dis));
    dfs1(1);
    u=1;
    for(int i=1;i<=n;i++)
    {
        if(dis[i]>dis[u])
        {
            u=i;
        }
        
    }
    memset(dis,-1,sizeof(dis));
    dis[u]=0;
    dfs1(u);
    v=u;
    for(int i=1;i<=n;i++)
    {
        if(dis[i]>dis[v]) v=i;
    }
    on[v]=1;
    dfs2(u);
    //以上步骤之后,所有点到u点的距离已知,接下来就用到u点的距离求答案
    dfs3(u,u);//处理不在直径上的点
    for(;u!=v;v=fa[v])
    {
        ans+=dis[v];
        s.push_back({u,v});
    }
    printf("%lld\n",ans);
    for(auto x:s)
    {
        printf("%lld %lld %lld\n",x[0],x[1],x[1]);
    }
}
posted @ 2024-04-24 15:59  Captainfly19  阅读(4)  评论(0编辑  收藏  举报