1. 程式人生 > >BZOJ4557 [JLoi2016]偵察守衛 【樹形dp】

BZOJ4557 [JLoi2016]偵察守衛 【樹形dp】

str 相互 數據 max define display 初始 覆蓋 play

題目鏈接

BZOJ4557

題解

orz
比較難的樹形dp
不過想想也還好

看數據猜狀態,一維是點,一維是D
那麽就先設\(f[i][j]\)表示\(i\)所在子樹已處理完畢,還能向上【或向任意方向】覆蓋\(j\)層的最小代價
考慮轉移,會發現子樹間會相互影響,一個子樹用\(f[s][j + 1]\)更新了\(f[i][j]\),其它的子樹就完全沒必要再用\(f[s‘][j + 1]\)去更新了,此時反而可以用\(f[i][j]\)來減少該子樹付出的代價
所以我們設一個\(g[i][j]\)表示\(i\)為根的子樹前\(j\)層待覆蓋,\(j\)層以下已處理完畢的最小代價

\(f[i][j]\)

包含\(f[i][j - 1]\),所以我們可以設狀態\(f[i][j]\)\(j\)表示小於等於\(j\)
同樣設\(g[i][j]\)中的\(j\)表示大於等於\(j\)

狀態轉移:枚舉子樹\(s\)
\[f[i][j] = min\{f[i][j] + g[s][j],f[s][j + 1] + g[i][j + 1]\}\]
\[g[i][j] += g[s][j - 1]\]

初始化就考慮\(i\)號點是不是一定被覆蓋,就可以確定\(f[i][0]\)\(g[i][0]\)的初值

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring> #include<algorithm> #define LL long long int #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); using namespace std; const
int maxn = 500005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int h[maxn],ne = 2; struct EDGE{int to,nxt;}ed[maxn << 1]; inline void build(int u,int v){ ed[ne] = (EDGE){v,h[u]}; h[u] = ne++; ed[ne] = (EDGE){u,h[v]}; h[v] = ne++; } int n,m,D,f[maxn][23],g[maxn][23],val[maxn],dan[maxn],fa[maxn]; int son[maxn],si; void dfs(int u){ for (int i = 1; i <= D; i++) f[u][i] = val[u]; if (dan[u]) f[u][0] = g[u][0] = val[u]; f[u][D + 1] = INF; Redge(u) if ((to = ed[k].to) != fa[u]){ fa[to] = u; dfs(to); for (int i = 0; i <= D; i++) f[u][i] = min(f[u][i] + g[to][i],f[to][i + 1] + g[u][i + 1]); for (int i = D; i >= 0; i--) f[u][i] = min(f[u][i],f[u][i + 1]); g[u][0] = f[u][0]; for (int i = 1; i <= D; i++) g[u][i] += g[to][i - 1]; for (int i = 1; i <= D; i++) g[u][i] = min(g[u][i],g[u][i - 1]); } } int main(){ n = read(); D = read(); REP(i,n) val[i] = read(); m = read(); REP(i,m) dan[read()] = true; for (int i = 1; i < n; i++) build(read(),read()); dfs(1); printf("%d\n",f[1][0]); return 0; }

BZOJ4557 [JLoi2016]偵察守衛 【樹形dp】