1. 程式人生 > >「6月雅禮集訓 2017 Day11」tree

「6月雅禮集訓 2017 Day11」tree

題目 -a stdio.h stdin amp pac out pan 樹形dp

【題目大意】

給出一棵帶權樹,有兩類點,一類黑點,一類白點。

求切斷黑點和白點間路徑的最小代價。

$n \leq 10^5$

【題解】

直接最小割能過。。但是樹形dp明顯更好寫

設$f_{x,0/1/2}$表示$x$這個點的子樹中,0表示沒有帶顏色的點連到這個子樹的根$x$,1表示黑點連到$x$,2表示白點連到$x$。

直接轉移即可。具體看代碼,挺好推得。。

技術分享
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>

using namespace
std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int N = 1e5 + 10, M = 2e5 + 10; const ll inf = 1e17; int n, d[N]; int head[N], nxt[M], to[M], w[M], tot = 0; inline void add(int u, int v, int _w) { ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; w[tot] = _w; } inline
void adde(int u, int v, int _w) { add(u, v, _w), add(v, u, _w); } ll f[N][3]; // 沒有有顏色點連到根上,只有黑點連到根上,只有白點連到根上 inline void dp(int x, int fa = 0) { f[x][0] = inf, f[x][1] = f[x][2] = 0; if(d[x] == 0) f[x][0] = 0; if(d[x] == 2) f[x][1] = inf; if(d[x] == 1) f[x][2] = inf; for (int i=head[x]; i; i=nxt[i]) {
if(to[i] == fa) continue; int y = to[i]; dp(y, x); f[x][0] += min(f[y][0], min(f[y][1], f[y][2]) + w[i]); if(f[x][0] > inf) f[x][0] = inf; f[x][1] += min(min(f[y][0], f[y][1]), f[y][2] + w[i]); if(f[x][1] > inf) f[x][1] = inf; f[x][2] += min(min(f[y][0], f[y][2]), f[y][1] + w[i]); if(f[x][2] > inf) f[x][2] = inf; } // printf("x = %d, f[x][0] = %I64d, f[x][1] = %I64d, f[x][2] = %I64d\n", x, f[x][0], f[x][1], f[x][2]); } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); cin >> n; for (int i=1, u, v, _w; i<n; ++i) { scanf("%d%d%d", &u, &v, &_w); adde(u, v, _w); } int T; cin >> T; for (int i=1, x; i<=T; ++i) { scanf("%d", &x); d[x] = 1; } cin >> T; for (int i=1, x; i<=T; ++i) { scanf("%d", &x); d[x] = 2; } dp(1); cout << min(f[1][0], min(f[1][1], f[1][2])); return 0; } /* 6 1 2 5 2 4 4 2 5 1 1 3 2 3 6 7 1 4 2 5 6 */
View Code

「6月雅禮集訓 2017 Day11」tree