CF1179D Fedor Runs for President

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qcwlmqy/article/details/102477947

CF1179D Fedor Runs for President


题意

  • 给一棵n个点的树,求加入一条边后,最多有多少条无向简单路径

  • 简单路径定义为任意一个点最多经过一次的路径


思路

  • 我们若将ij相连,我们可以将ij的路径拉直
    • 路径上每个点有一个以该点为根的子树,记子树大小为size[i]
    • 该路径上不同子树之间的点相互访问的简单路径增加了一条
    • 增加路径数 ( n s i z e [ i ] ) s i z e [ i ] = 1 2 ( n 2 s i z e [ i ] 2 ) \sum {(n-size[i])*size[i]} = \frac{1}{2} (n^2- \sum size[i]^2) i为路径上的点
    • 综上,我们要求的就是 s i z e [ i ] \sum size[i] 的最小值
  • 然后我们发现一个规律:路径的两端必定为叶子或者根
    • 若不为叶子,我可以让他的端点向子树方向扩展,那么得到size[i]更小( n 2 > x 2 + ( n x ) 2 n^2>x^2+(n-x)^2
  • dp[i]为以i为根的子树中 s i z e [ i ] \sum size[i] 最小的链
    • 转移方程为 d p [ i ] = m i n ( d p [ j ] + ( s i z e [ i ] s i z e [ j ] ) 2 ) dp[i]=min(dp[j]+(size[i]-size[j])^2)
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,可以将ij和并
    • a n s = m i n ( a n s , d p [ i ] + d p [ j ] + ( n s i z e [ i ] s i z e [ j ] ) 2 ) ans=min(ans,dp[i]+dp[j]+(n-size[i]-size[j])^2)
    • 可以将si合并, a n s = m i n ( d p [ i ] + ( n s i z e [ i ] ) 2 ) ans=min(dp[i]+(n-size[i])^2)
  • 对上面 n 2 n^2 做法显然会t,我们尝试斜率优化
    • a n s = d p [ i ] + d p [ j ] + ( n s i z e [ i ] s i z e [ j ] ) 2 ans = dp[i] + dp[j] + (n - size[i] - size[j]) ^ 2
    • d p [ j ] + ( n s i z e [ j ] ) 2 = 2 s i z e [ i ] ( n s i z e [ j ] ) + a n s s i z e [ i ] 2 d p [ i ] dp[j] + (n - size[j]) ^ 2 = 2 * size[i] * (n - size[j]) + ans - size[i] ^ 2 - dp[i]
    • x: n-size[j]
    • y : d p [ j ] + ( n s i z e [ j ] ) 2 y: dp[j] +(n-size[j])^2
    • k: 2*size[i]
    • b : a n s s i z e [ i ] 2 d p [ i ] b: ans- size[i] ^2 - dp[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;
}

猜你喜欢

转载自blog.csdn.net/qcwlmqy/article/details/102477947