假日住宿题解

    科技2022-07-14  130

    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]; } }
    Processed: 0.014, SQL: 8