CF911F Tree Destruction

CF911F Tree Destruction

题意:

(洛谷上写的中文题意太离谱了)

给定一棵树,要求选择两个节点,并把之间距离添加到答案中,然后删去一个点,再次进行这样的操作....

进行 \(n-1\) 次后,只剩一个顶点,询问最大答案,并且输出每次选择的两个点和删去的那个点。

分析:

什么情况下,树上两个点的距离最长:

当然是成直径的时候,我们根据这个理论推导,能口胡得到以下结论:

一个点在树上和另一个点距离最长,当且仅当这另一个点是树的两个直径之一(选最远的)

因此我们就有思路:

首先求出整个树的直径,深度,并且标记在直径上的点都有哪些。

深搜过程中,我们以直径上的点为 \(lca\) 节点,对整棵树进行搜索:

\(y\) 是直径上的点:

继续搜索,并把 \(lca\) 定为 \(y\)

\(y\) 不是直径上的点:

\(lca\) 不变,搜索到叶子节点 \(x\) 时,通过 \(lca\) 计算 \(x\) 距离直径两个端点 \(Ans,ans\) 的距离,选择距离更长的节点作为答案,记录到答案中,同时,记录方案,就是 \((x,ans/Ans,x)\)

最后再输出直径上的距离枚举方案,一个一个删就行,顺序没影响。

代码:

// CF911F Tree Destruction
#include<bits/stdc++.h>
using  namespace std;
#define int long long 
const int N=4e5+5;
vector<int> e[N];
int n,m,dep[N],fa[N];
int ans,T,Ans,final_ans,cnt;
bool vis[N];
struct answer{
    int a,b,c;
}A[N];
void dfs1(int x,int last){
    if(T==0&&dep[x]>dep[ans]) ans=x;
    if(T==1&&dep[x]>dep[Ans]) Ans=x;
    for(auto y:e[x]){
        if(y==last) continue;
        dep[y]=dep[x]+1;
        dfs1(y,x);
    }
}
void dfs2(int x,int last){
    for(auto y:e[x]){
        if(y==last) continue;
        dfs2(y,x);
        fa[y]=x; if(vis[y]) vis[x]=1;//判断是否在直径上
    }
}
void dfs3(int x,int last,int topfather){//统计不在直径上的点,比较离树的两个节点,取大的并且累加
    for(auto y:e[x]){
        if(y==last) continue;
        if(vis[y]) dfs3(y,x,y);//topfather是直径上的点
        else dfs3(y,x,topfather);
    }
    if(!vis[x]){
        if(dep[x]>dep[x]+dep[Ans]-(dep[topfather]*2))
            final_ans+=dep[x],A[++cnt]=(answer){x,ans,x};//ans 作为直径点最优
        else 
            final_ans+=dep[x]+dep[Ans]-(dep[topfather]*2),A[++cnt]=(answer){Ans,x,x};//Ans 作为直径点最优
    }
}
signed main(){
    cin>>n;
    for(int i=1,x,y;i<n;i++){
        scanf("%lld%lld",&x,&y); 
        e[x].push_back(y); e[y].push_back(x);
    }
    dfs1(1,0); memset(dep,0,sizeof(dep)); T++;
    dfs1(ans,0);//以ans 点为根节点计算深度
    vis[Ans]=1;
    dfs2(ans,0); dfs3(ans,0,ans);
    for(int i=Ans;i!=ans;i=fa[i]) {
        final_ans+=dep[i],A[++cnt]=(answer){ans,i,i};
    }
    cout<<final_ans<<endl;
    for(int i=1;i<=cnt;i++)
        printf("%lld %lld %lld\n",A[i].a,A[i].b,A[i].c);
    system("pause");
    return 0; 
}
posted @ 2021-09-28 16:17  Evitagen  阅读(55)  评论(0编辑  收藏  举报