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;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9