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();
}
posted @ 2026-08-17 20:10  acceptedK77  阅读(12)  评论(0)    收藏  举报