传送门:http://codeforces.com/problemset/problem/911/F
题意:
给你一棵树,每次选两个点,答案加上这两个点的距离,然后删除其中一个点,重复直到只剩下一个点,求最大的答案及每次的操作
题解:
(1)树的直径:树上最长的简单路径即为树的直径
(2)选择一个在直径外的点,与它距离最远的点一定是直径的两个端点之一
(3)每次从直径外找一个点,直到没有直径外的点,最后逐个删除直径上的点
代码:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
vector<int>g[200005];
int d[200005];
int pre[200005];
void dfs(int now,int fa,int dis)
{
pre[now]=fa;
d[now]=dis;
for(int i=0;i<g[now].size();i++)
{
if(g[now][i]!=fa)
dfs(g[now][i],now,dis+1);
}
}
tuple<int,int,int>ans[200005];
bool vis[200005];
long long sum;
int cnt;
int leaf=1;
int root=1;
void Find(int now,int ld,int rd,bool isfirst)
{
for(int i=0;i<g[now].size();i++)
{
if(!vis[g[now][i]]&&g[now][i]!=pre[now])
Find(g[now][i],ld+1,rd+1,false);
}
if(!isfirst)
{
if(ld>rd)
{
ans[cnt++]=make_tuple(root,now,now);
sum+=ld;
}else{
ans[cnt++]=make_tuple(leaf,now,now);
sum+=rd;
}
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
dfs(root,0,0);
for(int i=1;i<=n;i++)
{
if(d[root]<d[i])
root=i;
}
dfs(root,0,0);//两次dfs找树的直径
for(int i=1;i<=n;i++)
{
if(d[leaf]<d[i])
leaf=i;
}
cnt=0;
sum=0;
int p=leaf;
while(1)//递归找直径外的点
{
vis[p]=1;
Find(p,d[p],d[leaf]-d[p],1);
if(p==root)
break;
p=pre[p];
}
p=leaf;
while(p!=root)//直径内的点
{
ans[cnt++]=make_tuple(root,p,p);
sum+=d[p];
p=pre[p];
}
printf("%I64d\n",sum);
for(int i=0;i<cnt;i++)
{
int a,b,c;
tie(a,b,c)=ans[i];
printf("%d %d %d\n",a,b,c);
}
return 0;
}