1. 程式人生 > >luogu P2700 逐個擊破 樹dp

luogu P2700 逐個擊破 樹dp

傳送門

好題啊

給定邊權樹 求隔離所有指定點的最小花費

考慮樹dp的話 自然想到

f[x]表示子樹內處理完從根節點出發沒有敵人的最小花費 

g[x]表示子樹內處理完從根節點出發仍有敵人的最小花費 這個時候仍然合法()

又顯然根節點是否有敵人是有影響的 所以分類討論

 

首先子樹沒有敵人不用考慮

I. 根節點有敵人的話 f[x]就是inf

g[x]直接取f[v]和g[v]+cst[i]最小值

表示是否切x->v這條邊

II. 如果根節點沒有 

那麼g[x]維護的就是選擇一個花費最小的兒子切

而這樣的花費就是切掉其他所有兒子的花費加上這個的g[v]

所以我們回溯更新f[]和g[]之前先處理出子樹是否有敵人和切掉所有兒子的花費

 

方程看程式碼吧 強烈建議自己手推

注意這題開longlong!!!(我的30min啊..)

還有就是不要忘了初值還有讀入有0啥的 

Time cost : 75min

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5
#include<queue> 6 #include<vector> 7 #define itn int 8 #define ms(a,b) memset(a,b,sizeof a) 9 #define rep(i,a,n) for(int i = a;i <= n;i++) 10 #define per(i,n,a) for(int i = n;i >= a;i--) 11 #define inf 1e13 12 using namespace std; 13 typedef long long ll; 14 #define int ll 15 ll read() {
16 ll as = 0,fu = 1; 17 char c = getchar(); 18 while(c < '0' || c > '9') { 19 if(c == '-') fu = -1; 20 c = getchar(); 21 } 22 while(c >= '0' && c <= '9') { 23 as = as * 10 + c - '0'; 24 c = getchar(); 25 } 26 return as * fu; 27 } 28 //head 29 const int N = 100003; 30 int head[N],nxt[N<<1],mo[N<<1],cnt; 31 ll cst[N<<1]; 32 void _add(int x,int y,ll w) { 33 mo[++cnt] = y; 34 cst[cnt] = w; 35 nxt[cnt] = head[x]; 36 head[x] = cnt; 37 } 38 void add(int x,int y,ll w) {if(x^y)_add(x,y,w),_add(y,x,w);} 39 40 ll w[N]; 41 ll f[N],g[N]; 42 43 bool col[N],vis[N]; 44 void dfs(int x,int p) { 45 f[x] = g[x] = 0ll,vis[x] = col[x]; 46 ll tot = 0ll; 47 for(int i = head[x];i;i = nxt[i]) { 48 int sn = mo[i]; 49 if(sn == p) continue; 50 dfs(sn,x),vis[x] |= vis[sn]; 51 tot += min(f[sn],g[sn] + cst[i]); 52 } 53 54 if(col[x]) { 55 f[x] = inf; 56 for(int i = head[x];i;i = nxt[i]) { 57 int sn = mo[i]; 58 if(sn == p || !vis[sn]) continue; 59 if(col[sn]) g[x] += g[sn] + cst[i]; 60 else g[x] += min(f[sn],g[sn] + cst[i]); 61 } 62 63 } else { 64 g[x] = tot; 65 for(int i = head[x];i;i = nxt[i]) { 66 int sn = mo[i]; 67 if(sn == p || !vis[sn]) continue; 68 g[x] = min(g[x], tot - min(f[sn],g[sn] + cst[i]) + g[sn]); 69 if(col[sn]) f[x] += g[sn] + cst[i]; 70 else f[x] += min(f[sn],g[sn] + cst[i]); 71 } 72 } 73 } 74 75 signed main() { 76 int n = read(),k = read(); 77 rep(i,1,k) col[read()+1] = 1; 78 rep(i,2,n) { 79 int x = read() + 1,y = read() + 1; 80 add(x,y,read()); 81 } 82 dfs(1,1); 83 printf("%lld\n",min(f[1],g[1])); 84 return 0; 85 }
View Code

 

To be continued 這題還有生成樹的O(nlgn)?

雖然複雜度不優秀但是應該好想啊

再看看