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

P3267 [JLOI2016/SHOI2016]偵察守衛

long long col 接下來 連通 答案 code 地圖 return truct

$ \color{#0066ff}{ 題目描述 }$

小R和B神正在玩一款遊戲。這款遊戲的地圖由N個點和N-1條無向邊組成,每條無向邊連接兩個點,且地圖是連通的。換句話說,遊戲的地圖是一棵有N個節點的樹。

遊戲中有一種道具叫做偵查守衛,當一名玩家在一個點上放置偵查守衛後,它可以監視這個點以及與這個點的距離在D以內的所有點。這裏兩個點之間的距離定義為它們在樹上的距離,也就是兩個點之間唯一的簡單路徑上所經過邊的條數。在一個點上放置偵查守衛需要付出一定的代價,在不同點放置守衛的代價可能不同。

現在小R知道了所有B神可能會出現的位置,請你計算監視所有這些位置的最小代價。

\(\color{#0066ff}{輸入格式}\)

第一行包含兩個正整數N和D,分別表示地圖上的點數和偵查守衛的視野範圍。約定地圖上的點用1到N的整數編號。

第二行N個正整數,第i個正整數表示在編號為i的點放置偵查守衛的代價Wi。保證Wi<=1000。

第三行一個正整數M,表示B神可能出現的點的數量。保證M<=N。

第四行M個正整數,分別表示每個B神可能出現的點的編號,從小到大不重復地給出。

接下來N-1行,每行包含兩個正整數U,V,表示在編號為U的點和編號為V的點之間有一條無向邊。

\(\color{#0066ff}{輸出格式}\)

僅一行一個整數,表示監視所有B神可能出現的點所需要的最小代價

\(\color{#0066ff}{輸入樣例}\)

12 2
8 9 12 6 1 1 5 1 4 8 10 6
10
1 2 3 5 6 7 8 9 10 11
1 3
2 3
3 4
4 5
4 6
4 7
7 8
8 9
9 10
10 11
11 12

\(\color{#0066ff}{輸出樣例}\)

10

\(\color{#0066ff}{數據範圍與提示}\)

對於所有的數據,N<=500000,D<=20

\(\color{#0066ff}{題解}\)

對於這種在樹上覆蓋的問題,是一個比較經典的樹形DP模型

狀態\(f[i][j]\)表示以i為根子樹從i向下有j層未覆蓋的最小代價,j可以為負數,如果是負數,就是子樹全覆蓋,並向外覆蓋一些層

所以我們再開一個\(g[i][j]\)
表示以i為根子樹全覆蓋,並且向外覆蓋了j層的方案數

首先,對於必覆蓋點,初始化就是\(f[i][0]=g[i][0]=val[i]\)

註意當\(j\ge 1\)時,f覆蓋的範圍是不包括當前點的(j層未覆蓋),但是g包括了(i向外j全覆蓋)

所以\(g[i][j]\)也要初始化一下

考慮轉移

對於f,顯然有\(f[x][j] = min\{f[x][j-1]\}\)

還有就是子樹統計\(f[x][j]+=f[y][j-1]\)

對於g,顯然有\(g[x][i]=min\{g[x][i+1]\}\)

還有,就是考慮x的外面由哪個子樹覆蓋,這個覆蓋包括了x子樹內部,\(g[y][j]\)覆蓋了\(f[x][j]\)未覆蓋的j層

\(g[x][j]=min\{f[x][j+1]+g[y][j+1],f[y][j]+g[x][j]\}\)

答案就是\(f[1][0]\)

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 5e5 + 100;
const int inf = 0x7fffffff;
int f[maxn][25], g[maxn][25], val[maxn];
int n, d, m;
bool vis[maxn];
struct node {
    int to;
    node *nxt;
    node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
void add(int from, int to) { head[from] = new node(to, head[from]); }
void dfs(int x, int fa) {
    if(vis[x]) f[x][0] = g[x][0] = val[x];
    for(int i = 1; i <= d; i++) g[x][i] = val[x];
    g[x][d + 1] = inf;
    for(node *i = head[x]; i; i = i->nxt) {
        if(i->to == fa) continue;
        dfs(i->to, x);
        for(int j = 0; j <= d; j++) g[x][j] = std::min(g[x][j] + f[i->to][j], g[i->to][j + 1] + f[x][j + 1]);
        for(int j = d - 1; j >= 0; j--) g[x][j] = std::min(g[x][j], g[x][j + 1]);
        f[x][0] = g[x][0];
        for(int j = 1; j <= d; j++) f[x][j] += f[i->to][j - 1];
        for(int j = 1; j <= d; j++) f[x][j] = std::min(f[x][j], f[x][j - 1]);
    }
}

int main() {
    n = in(), d = in();
    for(int i = 1; i <= n; i++) val[i] = in();
    m = in();
    for(int i = 1; i <= m; i++) vis[in()] = true;
    int x, y;
    for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
    dfs(1, 0);
    printf("%d\n", f[1][0]);
    return 0;
}

P3267 [JLOI2016/SHOI2016]偵察守衛