LG P3267 [JLOI2016/SHOI2016]侦察守卫

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88627942

题目
这个题 d p dp 的状态和大概的方法很好想,但是在付诸实践时却有点。。。
f i , j f_{i,j} 表示处理完 i i 为根的子树后, i i 的子树内没被监视的点中深度最大的离 i i 的距离为 j j
g i , j g_{i,j} 表示处理完 i i 为根的子树后,还可以从 i i 往上监视 j j 的距离。
可以得到: g i , j = min u s o n i ( g u , j + 1 + v s o n i   a n d   v ! = u f v , j 1 ) g_{i,j} = \min_{u \in son_i}(g_{u,j+1}+\sum_{v\in son_i \ and \ v!=u } f_{v,j-1})
然后,。。。怎么写啊。。。。。
我们不要再把树形DP的状态简单理解为一个点和一些附加信息了。
树形DP的DP顺序是按照儿子一个一个加入的。
如果不是,像上面的式子会像无形之刃,爆零预定。
我们考虑加入儿子时,设父亲为 u u ,加入儿子 v v 前的为 f v , u , j f_{v,u,j} g v , u , j g_{v,u,j} ,因为~~可以预见 ~~转移可以原地转移,可以把 v v 这一维省掉。
最后变成:
g u , i = min ( g u , i + f v , i 1 , f u , i + g v , i + 1 ) g_{u,i} = \min(g_{u,i}+f_{v,i-1},f_{u,i}+g_{v,i+1})
f u , i = f u , i + f v , i 1 f_{u,i} = f_{u,i} + f_{v,i-1}
其实上面的没有考虑很多情况。
深度最大的不一定是 j j ,可以比 j j 低。
来一发前缀最小值。
f i , j f_{i,j} 的意义改为距离 < = j <=j , g i , j g_{i,j} 的意义改为距离 > = j >=j
那么。
上面的式子还有情况没有考虑到。
如果子树内没有需要控制的点呢?
我们需要一个 f i , f_{i,-\infty}
f f 整体右移一位,用 f i , 0 f_{i,0} f i , f_{i,-\infty} 就行。

特别注意将儿子考虑在树形DP的状态中,不然DDP怎么打啊。

AC Code:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define maxn 500002
#define o 22
#define inf 0x3f3f3f3f
using namespace std;

int n,d,m;
int f[maxn][25],g[maxn][25],w[maxn],tag[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;}

void dfs(int now,int ff){
    if(tag[now]) f[now][0] = g[now][0] = w[now];
    for(int i=1;i<=d;i++) g[now][i] = w[now];
    g[now][d+1] = inf;
    for(int i=info[now];i;i=Prev[i])
        if(to[i]!=ff)
            dfs(to[i],now);
    for(int i=info[now];i;i=Prev[i])
        if(to[i]!=ff){
            for(int j=d;j>=0;j--) g[now][j] = min(g[now][j]+f[to[i]][j],f[now][j+1]+g[to[i]][j+1]);
            for(int j=d;j>=0;j--) g[now][j] = min(g[now][j] , g[now][j+1]);
            f[now][0] = g[now][0];
            for(int j=1;j<=d+1;j++) f[now][j] +=f[to[i]][j-1];
            for(int j=1;j<=d+1;j++) f[now][j] = min(f[now][j] , f[now][j-1]);
        }
}

int main(){
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    scanf("%d",&m);
    for(int i=1,x;i<=m;i++) scanf("%d",&x),tag[x]=1;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        Node(u,v),Node(v,u);
    }
    dfs(1,0);
    printf("%d\n",f[1][0]);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88627942