1. 程式人生 > >luoguP3267 [JLOI2016/SHOI2016] 偵查守衛 樹dp

luoguP3267 [JLOI2016/SHOI2016] 偵查守衛 樹dp

傳送門

今天開始儘量補一補欠的70多道題的blog...(菜啊)

都是幾句話的事 週末大概就完了

 

一看不是樹dp經典題嗎 最小覆蓋

然後告訴我有些點不必覆蓋?

20minutes later 發現其實是一樣的...因為不必覆蓋不是一定不能覆蓋

其實是我一開始想成記錄放的節點個數那個題 然後狀態設計跪了

還是說一下陣列吧:

f[i][j]表示從i點向下 已知 的子樹最多延伸j步沒有覆蓋的最小花費

g[i][j]表示從i點向上 已知 的子樹最多能延伸j步覆蓋其他點的最小花費

自然遍歷兒子的時候先遞迴 回溯的時候更新

先是g[x][i]

逆序更新

我們要加上現在這個子樹所有的點 兩種情況:

g[x][i] = g[x][i] + f[sn][i] 利用其他子樹覆蓋 最多覆蓋j的距離所以j以下由後面那個f[][]覆蓋

g[x][i] = g[sn][i+1] + f[x][i+1] 利用這個子樹覆蓋 可以覆蓋其他子樹i-1的距離 i及一下由後面那個f[][]覆蓋

然後取最小值

(自己畫個圖就OK)

然後更新f[x][i]

順序更新

首先f[x][0] = g[x][0](意義相同)

然後f[x][i] = ∑(v∈sn)f[sn][i-1] 同上 i-1步以下要覆蓋好

同時g[x][i]  = min(g[x][i],g[x][i+1]) f[x][i] = min(f[x][i],f[x][i-1]) (可以看做是後面兩項的退化)

然後就是初始化 如果要求覆蓋該點那麼f[x][0] = g[x][0] = w[x] 

同時更新之前先判斷x放點 也就是g[x][i] = w[x]

(沒想到小細節一說出來挺多的...)

注意g[x][d+1] = inf....

不要設f[]g[]初值是inf....inf不要太大.....

Time cost: 55min

Code:

 1 #include<cstdio>
 2
#include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<iostream> 6 #include<cmath> 7 #define ms(a,b) memset(a,b,sizeof a) 8 #define rep(i,a,n) for(int i = a;i <= n;i++) 9 #define per(i,n,a) for(int i = n;i >= a;i--) 10 #define inf 1e9+7 11 using namespace std; 12 typedef long long ll; 13 typedef double D; 14 #define eps 1e-8 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 = 500005; 30 int n,d; 31 int head[N],nxt[N<<1],mo[N<<1],cnt; 32 void _add(int x,int y) { 33 mo[++cnt] = y; 34 nxt[cnt] = head[x]; 35 head[x] = cnt; 36 } 37 void add(int x,int y) {if(x^y)_add(x,y),_add(y,x);} 38 bool vis[N]; 39 int w[N],f[N][21],g[N][21]; 40 41 void dfs(int x,int p) { 42 if(vis[x]) f[x][0] = g[x][0] = w[x]; 43 rep(i,1,d) g[x][i] = w[x]; 44 g[x][d+1] = (int)inf; 45 for(int i = head[x];i;i = nxt[i]) { 46 int sn = mo[i]; 47 if(sn == p) continue; 48 dfs(sn,x); 49 per(i,d,0) g[x][i] = min(g[x][i] + f[sn][i],f[x][i+1] + g[sn][i+1]); 50 per(i,d,0) g[x][i] = min(g[x][i],g[x][i+1]); 51 f[x][0] = g[x][0]; 52 rep(i,1,d+1) f[x][i] += f[sn][i-1]; 53 rep(i,1,d+1) f[x][i] = min(f[x][i],f[x][i-1]); 54 } 55 } 56 57 int main() { 58 n = read(),d = read(); 59 rep(i,1,n) w[i] = read(); 60 int m = read(); 61 rep(i,1,m) vis[read()] = 1; 62 rep(i,2,n) add(read(),read()); 63 dfs(1,1); 64 printf("%d\n",f[1][0]); 65 return 0; 66 }
View Code