CodeForces - 911F
头昏
CodeForces - 911F
题意
给定一棵大小为 \(n\) 的树,有 \(n-1\) 次操作,每次选择两个叶子节点,将分数加上两点间边的数量,并选择删除其中一个点。
求分数最大值,及其操作方法。
思路
树的直径:你好。
记树的直径两个端点分别是 \(x\) 和 \(y\)。
每个点与之距离最大的点,一定是树的直径两个端点之一,即 \(x\) 或 \(y\)。
先把不在直径上的点删了,保留 \(x\) 和 \(y\),最后将树的直径依次删完。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const ll maxn=2e6+5;
int n,m;
vector<int> e[maxn];
int dep[maxn];
void dfs(int now,int lst) {
dep[now]=dep[lst]+1;
for (int nxt:e[now]) {
if (nxt==lst) continue;
dfs(nxt,now);
}
}
ll ans=0;
struct nd {
int a,b,c;
};
int x,y;
vector<nd> v,v1;
void dfs2(int now,int lst,int fa) {
for (int nxt:e[now]) {
if (nxt==lst) continue;
dfs2(nxt,now,fa);
}
ans+=max(dep[now],dep[now]-dep[fa]+dep[y]-dep[fa]+1);
if (dep[now]>dep[now]-dep[fa]+dep[y]-dep[fa]+1) v.push_back({now,x,now});
else v.push_back({now,y,now}); //儿子删完了当前点才能删,所以push_back放这里
}
void dfs1(int now,int lst) {
if (now==x) return ;
v1.push_back({now,x,now});//直径上的点删除时单独记,push_back放哪无所谓
ans+=dep[now];
for (int nxt:e[now]) {
if (nxt==lst) continue;
if (dep[nxt]<dep[now]) dfs1(nxt,now);
else dfs2(nxt,now,now);
}
}
void solve() {
cin>>n;
for (int i=1;i<n;i++) {
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
for (int i=1;i<=n;i++) if (dep[x]<dep[i]) x=i;
memset(dep,0,sizeof(dep));
dfs(x,0);
for (int i=1;i<=n;i++) if (dep[y]<dep[i]) y=i;
dfs1(y,0);
cout<<ans-n+1<<'\n';
for (nd ND:v) cout<<ND.a<<' '<<ND.b<<' '<<ND.c<<'\n';
for (nd ND:v1) cout<<ND.a<<' '<<ND.b<<' '<<ND.c<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T=1;
while (T--) solve();
}

浙公网安备 33010602011771号