1. 程式人生 > >Luogu 3267 [JLOI2016/SHOI2016]偵察守衛

Luogu 3267 [JLOI2016/SHOI2016]偵察守衛

cst htm tar 只需要 現在 圖片 alt pre head

以後要記得復習鴨

BZOJ 4557

大佬的博客

狀態十分好想,設$f_{x, i}$表示以覆蓋完$x$為根的子樹後還能向上覆蓋$i$層的最小代價,$g_{x, i}$表示以$x$為根的子樹下深度為$i$還沒有被覆蓋的最小代價。

那麽對於每一個關鍵點,有初態: $f_{x,0} = g_{x, 0} = val_x$。

對於不是關鍵點的點,有:$f_{x, i} = val_x$ $0 \leq i \leq d$ $f_{x, d + 1} = inf$。

然後就不會了

感覺關鍵是維護$f_x$和$g_x$的單調性,因為$f_{x, i}$一定不會大於$f_{x, i + 1}$,而$g_{x, i}$一定不會大於$g_{x, i - 1}$。

我們考慮對$x$的所有兒子$y$分開考慮,對於每一個$y$,有方程

    $f_{x, i} = min(f_{x, i} + g_{y, i}, g_{x, i + 1} + f_{y, i + 1})$。

前者表示在之前的計算中已經取了一個子結點能覆蓋到$x$,現在這個結點只需要取$g_{y, i}$即可,後者表示取這個結點的$f_{y, i + 1}$,而剩下的代價最小為$g_{x, i + 1}$。

記得從上到下枚舉$i$。

然後再掃一遍維護$f_{x}$的單調性。

接著考慮計算$g$,有$g_{x, 0} = f_{x, 0}$。

對於每一個$y$,有$g_{x, i} += g_{y, i - 1}$。

然後從下到上掃一遍維護$g_x$的單調性。

時間復雜度$O(nd)$。

Code:

技術分享圖片
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 5e5 + 5;
const int M = 22;
const int inf = 1 << 30;

int n, m, d, a[N], tot = 0, head[N], f[N][M], g[N][M]; 
bool flag[N];

struct Edge {
    int to, nxt;
} e[N 
<< 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > 9 || ch < 0; ch = getchar()) if(ch == -) op = -1; for(; ch >= 0 && ch <= 9; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int min(int x, int y) { return x > y ? y : x; } inline void chkMin(int &x, int y) { if(y < x) x = y; } void dfs(int x, int fat) { if(flag[x]) g[x][0] = f[x][0] = a[x]; for(int i = 1; i <= d; i++) f[x][i] = a[x]; f[x][d + 1] = inf; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x); for(int j = d; j >= 0; j--) f[x][j] = min(f[x][j] + g[y][j], g[x][j + 1] + f[y][j + 1]); for(int j = d; j >= 0; j--) chkMin(f[x][j], f[x][j + 1]); g[x][0] = f[x][0]; for(int j = 1; j <= d; j++) g[x][j] += g[y][j - 1]; for(int j = 1; j <= d; j++) chkMin(g[x][j], g[x][j - 1]); } } int main() { read(n), read(d); for(int i = 1; i <= n; i++) read(a[i]); read(m); for(int x, i = 1; i <= m; i++) { read(x); flag[x] = 1; } for(int x, y, i = 1; i < n; i++) { read(x), read(y); add(x, y), add(y, x); } dfs(1, 0); printf("%d\n", f[1][0]); return 0; }
View Code

Luogu 3267 [JLOI2016/SHOI2016]偵察守衛