假日住宿題解

IDSY QAQ發表於2020-10-04

1.題面

2.前言

太妙了啊太妙了

3.分析

大體思路:列舉每一條邊,每一條邊都對應著兩個點 ( u , v ) (u, v) (u,v) ,從這條邊斷開後,將出現以 u u u , v v v 為根節點的兩顆樹,我們就將這條邊最大化利用,就是將在左子樹的人儘可能多的移動到右子樹去(這樣一定會經過 e d g e ( u , v ) edge(u, v) edge(u,v) ),右子樹同理,由於每個城市只能由一個人,所以在兩顆樹上的人交換過去的數量為兩棵子樹節點數量的最小值。

ATTENTION :為什麼這樣做不會出現一個人多次走一條邊的情況呢?因為我們列舉的是邊,假設的是讓兩棵樹上的人交換,所以之後我們不會再遍歷這條邊了,又因為道路是一棵樹,所以不會出現環,所以也不會出現走回原點的情況。

4.參考程式碼

#include <cstdio>
#include <cstring>
#define LL long long

const int MAXN = 1e5 + 5;

int t, n;
LL ans;
LL dp[MAXN];

int len;
int head[MAXN];
struct edge {
    int to, next, val;
}e[MAXN * 2];//切記乘以2

void add (int, int, int);//鏈式前向星儲存邊
void dfs (int, int);//樹形dp求出以每個節點為根節點的子樹的大小
void init ();//初始化

LL Min (LL x, LL y) { return x < y ? x : y; }

int main () {
    scanf ("%d", &t);
    while (t--) {
        init ();
        scanf ("%d", &n);
        for (int i = 1; i < n; i++) {
            int x, y, val; scanf ("%d %d %d", &x, &y, &val);
            add (x, y, val); add (y, x, val);
        }
        dfs (1, -1);
        printf ("%lld\n", ans);
    }
    return 0;
}

void init () {
    memset (head, 0, sizeof head);
    memset (dp, 0, sizeof dp);
    ans = 0; len = 0;
}

void add (int x, int y, int val) {
    e[++len].to = y;
    e[len].next = head[x];
    e[len].val = val;
    head[x] = len;
}

void dfs (int u, int father) {
    dp[u] = 1;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to, w = e[i].val;
        if (v == father) continue;
        dfs (v, u);
        ans += 2 * w * Min (dp[v], n - dp[v]);//一顆子樹的大小為dp[v],另一顆子樹的大小就為n - dp[v]了
        dp[u] += dp[v];
    }
}

相關文章