版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
CF1179D Fedor Runs for President
题意
-
给一棵
n
个点的树,求加入一条边后,最多有多少条无向简单路径 -
简单路径定义为任意一个点最多经过一次的路径
思路
- 我们若将
i
和j
相连,我们可以将i
到j
的路径拉直- 路径上每个点有一个以该点为根的子树,记子树大小为
size[i]
- 该路径上不同子树之间的点相互访问的简单路径增加了一条
- 增加路径数
,
i
为路径上的点 - 综上,我们要求的就是 的最小值
- 路径上每个点有一个以该点为根的子树,记子树大小为
- 然后我们发现一个规律:路径的两端必定为叶子或者根
- 若不为叶子,我可以让他的端点向子树方向扩展,那么得到
size[i]
更小( )
- 若不为叶子,我可以让他的端点向子树方向扩展,那么得到
- 令
dp[i]
为以i
为根的子树中 最小的链- 转移方程为
n_size[now] = 1;
for (auto it : E[now])
{
if (it == fa)
continue;
dfs(it, now);
n_size[now] += n_size[it];
}
dp[now] = n_size[now] * n_size[now];
for (auto it : E[now])
{
if (it == fa)
continue;
dp[now] = min(dp[now], dp[it] + (n_size[now] - n_size[it]) * (n_size[now] - n_size[it]));
}
- 对于根
s
,可以将i
,j
和并- 可以将
s
与i
合并,
- 对上面
做法显然会
t
,我们尝试斜率优化x: n-size[j]
k: 2*size[i]
register LL x, y, k, b;
q[top = 1] = make_pair(n - s[1].first, s[1].second + (n - s[1].first) * (n - s[1].first));
for (int i = 2; i <= tot; i++)
{
x = n - s[i].first;
y = s[i].second + x * x;
k = 2 * s[i].first;
int pos = binary_search(k);
b = q[pos].second - k * q[pos].first;
ans = min(ans, b + s[i].first * s[i].first + s[i].second);
//(y2-y1)/(x2-x1) >= (y3-y1)/(x3-x1)
while (top > 1 && (q[top].second - q[top - 1].second) * (x - q[top - 1].first) >= (y - q[top - 1].second) * (q[top].first - q[top - 1].first))
top--;
q[++top] = make_pair(x, y);
}
ans = min(ans, dp[now] + (n - n_size[now]) * (n - n_size[now]));
a[now] = make_pair(n_size[now], dp[now]);
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
typedef long long LL;
LL n;
vector<int> E[maxn];
LL n_size[maxn], dp[maxn];
//dp表示以 i 为根的子数,连接叶子节点 子树平方和最小的路径
typedef pair<LL, LL> pll;
bool cmp(pll& a, pll& b)
{
return a.first > b.first;
}
pll a[maxn], s[maxn], q[maxn];
int top;
LL binary_search(LL k)
{
if (top == 1)
return 1;
int l = 2, r = top, mid, ans = 1;
while (l <= r) {
mid = (l + r) >> 1;
if ((q[mid].second - q[mid - 1].second) <= k * (q[mid].first - q[mid - 1].first))
ans = max(ans, mid),
l = mid + 1;
else
r = mid - 1;
}
return ans;
}
LL ans = 1e18;
void dfs(int now, int fa)
{
n_size[now] = 1;
for (auto it : E[now]) {
if (it == fa)
continue;
dfs(it, now);
n_size[now] += n_size[it];
}
dp[now] = n_size[now] * n_size[now];
for (auto it : E[now]) {
if (it == fa)
continue;
dp[now] = min(dp[now], dp[it] + (n_size[now] - n_size[it]) * (n_size[now] - n_size[it]));
}
// ans = dp[i] + dp[j] + (n - size[i] - size[j]) ^ 2
// dp[j] + (n - size[j]) ^ 2 = 2 * size[i] * (n - size[j]) + ans - size[i] ^ 2 - dp[i]
int tot = 0;
for (auto it : E[now]) {
if (it == fa)
continue;
s[++tot] = a[it];
}
// x: n-size[j]
// y: dp[j] +(n-size[j])^2
// k: 2*size[i]
// b: ans- size[i] ^2 - dp[i]
sort(s + 1, s + 1 + tot, cmp);
int l = 1, r = 1;
register LL x, y, k, b;
q[top = 1] = make_pair(n - s[1].first, s[1].second + (n - s[1].first) * (n - s[1].first));
for (int i = 2; i <= tot; i++) {
x = n - s[i].first;
y = s[i].second + x * x;
k = 2 * s[i].first;
int pos = binary_search(k);
b = q[pos].second - k * q[pos].first;
ans = min(ans, b + s[i].first * s[i].first + s[i].second);
//(y2-y1)/(x2-x1) >= (y3-y1)/(x3-x1)
while (top > 1 && (q[top].second - q[top - 1].second) * (x - q[top - 1].first) >= (y - q[top - 1].second) * (q[top].first - q[top - 1].first))
top--;
q[++top] = make_pair(x, y);
}
ans = min(ans, dp[now] + (n - n_size[now]) * (n - n_size[now]));
a[now] = make_pair(n_size[now], dp[now]);
}
int main()
{
// freopen("out.txt", "r", stdin);
scanf("%I64d", &n);
int u, v;
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
E[u].push_back(v);
E[v].push_back(u);
}
dfs(1, 0);
//cout << ans << '\n';
printf("%I64d\n", n * (n - 1) / 2 + (n * n - ans) / 2);
return 0;
}