题解:CF1179D Fedor Runs for President

题意:给出一棵树,要求你加一条边,使得图上简单路径最多。

做法:

我们先刻画一下加一条边后路径个数是多少,发现只有对于两个点都不在环上且向环走最后到的节点相同,也就是在同一颗子树内的点没有增加路径。

那么我们回到树上,我们就等同于选择两个点之间连一条边,环即是这条边和他们在树上的路径所形成的环。我们记 \(f_u\) 为点 \(u\) 被选入路径后不向环上拓展的子树大小。那么我们假设选择的两个点为 \(x,y\),稍微讨论就会发现答案应该长成这样:

\[n^2-\sum_{u\in path(x,y)}s_u ^ 2 \]

解释一下为什么不考虑单点,因为在这种情况下,刚好每个单点都会被加一次减一次,不会有贡献。

既然是一个树上路径的东西,那么我们可以考虑枚举 lca 或者淀粉质,因为这里的信息比较简单我们考虑枚举 lca。

首先后面那一坨东西看着就很丑,我们考虑化成几个和式的形式。

假设 lca 为 \(u\),注意到对于 \(u\)\(x,y\) 方向上的链上的贡献都是 \((sz_{v} - sz_{son_{v}}) ^ 2\) 的形式,与上方无关,所以我们可以考虑直接 dp 出来,记每个子树内的最大的价值为 \(val_u\)

那么考虑枚举 lca 为 \(u\),首先第一种情况是我们直接连接 lca 和子树内的的一个点,这个就直接枚举是哪个儿子 \(v\),然后直接计算 \((n-sz_v) ^2 + val_v\),直接枚举即可。

问题在于第二种,我们有两个儿子,这个柿子为 \((n-sz_x-sz_y)^2+val_x+val_y\),跟两个有关系。

我们考虑经典的处理手法,我们先将变量分离,发现跟 \(x,y\) 同时有关的是 \(2sz_xsz_y\),那么我们可以枚举一个 \(x\) 去找出最优的对应的 \(y\),发现这个东西是个一次函数状物,所以我们可以直接上斜率优化即可。

复杂度 \(O(n\log n)\),瓶颈在于给儿子排序。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5e5 + 5, mod = 998244353;
const double eps = 1e-8;
int n, sz[maxn], dp[maxn];
vector<int> e[maxn];
int X(int i) {
	return -2 * sz[i];
}
int Y(int i) {
	return dp[i] + sz[i] * sz[i] - 2 * n * sz[i];
}
double slope(int i, int j) {
	return (Y(j) - Y(i)) / (X(i) != X(j) ? X(j) - X(i) : eps);
}
int q[maxn], f, t;
void dfs1(int u, int fa) {
	sz[u] = 1;
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		dfs1(v, u);
		sz[u] += sz[v];
	}
	dp[u] = sz[u] * sz[u];
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		dp[u] = min(dp[u], dp[v] + (sz[u] - sz[v]) * (sz[u] - sz[v]));
	}
//	cout << u << " " << dp[u] << endl;
}
int ans = 9e18;
bool cmp(int x, int y) {
	return sz[x] > sz[y];
}
void dfs2(int u, int fa) {
	sort(e[u].begin(), e[u].end(), cmp);
	ans = min(ans, dp[u] + (n - sz[u]) * (n - sz[u]));
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		dfs2(v, u);
	}
	f = t = 0;
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		while(f < t - 1 && slope(q[t - 2], q[t - 1]) < sz[v])
			f++;
		if(f < t) 
			ans = min(ans, dp[q[f]] + dp[v] + (n - sz[q[f]] - sz[v]) * (n - sz[q[f]] - sz[v]));
		while(f < t - 1 && slope(q[t - 2], q[t - 1]) > slope(q[t - 1], v))
			t--;
		q[t++] = v;
	}
}
signed main() {
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y; cin >> x >> y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs1(1, 0);
	dfs2(1, 0);
	cout << (n * (n - 1) + n * n - ans) / 2 << endl;
	return 0;
}
posted @ 2025-08-11 23:20  LUlululu1616  阅读(8)  评论(0)    收藏  举报