1. 程式人生 > 實用技巧 >【2019 CCPC 江西省賽】Cotree 樹重心

【2019 CCPC 江西省賽】Cotree 樹重心

題目連結

題意

給出 \(n\) 個頂點,\(n-2\) 條邊。也就是兩顆樹,現在讓連一條邊,變成一顆樹,使得 \(\sum_{i=1}^{n}\sum_{j=i+1}^{n}dis(i,j)\) 最小。

瞎嗶嗶

看完題盲猜一波把兩棵樹的重心連到一起。寫起來太麻煩,而且不保證結論正不正確。

就去寫了一個等腰梯形的題目,然後由於自己把題意讀錯,瘋狂WA,最後yzj隊,把A題一過,跑我們上面去了。

一看他們過,我猜結論應該是正確的。
yzj說就剩一小時了,這題程式碼挺長的,你們應該過不了了。

一聽,這這這。

開始瘋狂敲程式碼,半小時寫完,小小的改了一下bug,過了樣例,一交過了。

真爽

題解

先求出兩顆樹的重心,連起來。

如何算這個呢?
\(\sum_{i=1}^{n}\sum_{j=i+1}^{n}dis(i,j)\)

我們列舉邊的貢獻。

對於每條邊邊它的貢獻為:\(sz[u]*(n-sz[u])\),即左右兩邊的頂點數量相乘。

程式碼

/*
 * @Autor: valk
 * @Date: 2020-08-11 12:38:37
 * @LastEditTime: 2020-10-16 13:32:27
 * @Description: 如果邪惡  是華麗殘酷的樂章 它的終場 我會親手寫上 晨曦的光 風乾最後一行憂傷 黑色的墨 染上安詳
 */
#include <bits/stdc++.h>
#define fuck system("pause")
#define pb emplace_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;

int sz[N], mxsz[N];
vector<int> vec[N];
int vis[N], n, m;
void solve(int u, int fa)
{
    ++m;
    vis[u] = 1;
    for (int v : vec[u]) {
        if (v == fa || vis[v])
            continue;
        solve(v, u);
    }
}

int dfs(int u, int fa)
{
    sz[u] = 1;
    for (int v : vec[u]) {
        if (v == fa)
            continue;
        dfs(v, u);
        sz[u] += sz[v];
        mxsz[u] = max(mxsz[u], sz[v]);
    }
    if (vis[u] == 0)
        mxsz[u] = max(mxsz[u], n - sz[u]);
    else
        mxsz[u] = max(mxsz[u], m - sz[u]);
}

int dfs1(int u, int fa)
{
    sz[u] = 1;
    for (int v : vec[u]) {
        if (v == fa)
            continue;
        dfs1(v, u);
        sz[u] += sz[v];
    }
}
ll rel = 0;
int dfs2(int u, int fa)
{
    for (int v : vec[u]) {
        if (v == fa)
            continue;
        rel += 1LL * sz[v] * (n - sz[v]);
        dfs2(v, u);
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n - 2; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        vec[u].pb(v), vec[v].pb(u);
    }
    solve(1, 1); //染色
    n -= m;
    int rt1, rt2;
    for (int i = 1; i <= n + m; i++) {
        if (vis[i] == 1) {
            rt1 = i;
            break;
        }
    }
    for (int i = 1; i <= n + m; i++) {
        if (vis[i] == 0) {
            rt2 = i;
            break;
        }
    }
    dfs(rt1, rt1);
    int minn = inf;
    for (int i = 1; i <= n + m; i++) {
        if (vis[i] == 1)
            minn = min(minn, mxsz[i]);
    }
    int w1, w2;
    for (int i = 1; i <= n + m; i++) {
        if (vis[i] == 1 && minn == mxsz[i]) {
            w1 = i;
        }
    }
    dfs(rt2, rt2);
    minn = inf;
    for (int i = 1; i <= n + m; i++) {
        if (!vis[i]) {
            minn = min(minn, mxsz[i]);
        }
    }
    for (int i = 1; i <= n + m; i++) {
        if (vis[i] == 0 && minn == mxsz[i]) {
            w2 = i;
        }
    }
    vec[w1].pb(w2), vec[w2].pb(w1);
    memset(sz, 0, sizeof(sz));
    n += m;
    dfs1(1, 1);
    dfs2(1, 1);
    printf("%lld\n", rel);
    return 0;
}