LG P3267 [JLOI2016/SHOI2016]侦察守卫

39 篇文章 0 订阅
博客介绍了如何使用树形动态规划(DP)解决一道名为LG P3267的题目,涉及侦察守卫的部署策略。作者探讨了状态定义、转移方程,并提到了在实现过程中要注意避免错误的理解和处理特殊情况,如深度最大值不一定等于目标距离。通过调整状态和利用前缀最小值优化,最终实现了AC(Accepted)代码。
摘要由CSDN通过智能技术生成

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

特别注意将儿子考虑在树形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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值