bzoj4557 [JLoi2016]侦察守卫(树形dp)

这个树形dp没想出来…好菜orz
首先考虑如果覆盖了一个点x,那么对于他的一个儿子y来说,它的子树内的前D-1层可以不用覆盖了,只需覆盖剩余点即可。
因此我们考虑设计状态f[x][j],表示x子树外面的点已经帮着覆盖了前j层,覆盖子树内剩余必须点的最小花费。(x为第一层)
转移也很明显: f[x][j]+=f[y][j1] f [ x ] [ j ] + = f [ y ] [ j − 1 ]
但是我们发现一个问题,f[x][0]怎么计算呢?即最小代价怎么算呢,毕竟不同的子树之间还会影响…
于是我们增设状态g[x][j],表示x子树被完全覆盖,且覆盖了x外面j层的最小花费。(x为外面第0层)
可以用f和g来更新g,然后f[x][0]=g[x][0]。
然后就可以转移了
复杂度 O(nm) O ( n m )

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define N 500010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,h[N],num=0,w[N],f[N][22],g[N][22];
//f[x][j],x子树外面的点已经帮着覆盖了前j层,覆盖子树内剩余必须点的最小花费。(x为第一层)
//g[x][j],x子树被完全覆盖,且覆盖了x外面j层的最小花费。(x为外面第0层)
bool mk[N];
struct edge{
    int to,next;
}data[N<<1];
void dfs(int x,int Fa){
    f[x][0]=g[x][0]=mk[x]?w[x]:0;
    for(int i=1;i<=m;++i) g[x][i]=w[x];g[x][m+1]=inf;
    for(int ii=h[x];ii;ii=data[ii].next){
        int y=data[ii].to;if(y==Fa) continue;dfs(y,x);
        for(int i=0;i<=m;++i) g[x][i]=min(g[x][i]+f[y][i],g[y][i+1]+f[x][i+1]);
        for(int i=m;i>=0;--i) g[x][i]=min(g[x][i],g[x][i+1]);
        f[x][0]=g[x][0];
        for(int i=1;i<=m;++i) f[x][i]+=f[y][i-1];
        for(int i=1;i<=m;++i) f[x][i]=min(f[x][i],f[x][i-1]);
    }
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;++i) w[i]=read();
    int owo=read();
    while(owo--) mk[read()]=1;
    for(int i=1;i<n;++i){
        int x=read(),y=read();
        data[++num].to=y;data[num].next=h[x];h[x]=num;
        data[++num].to=x;data[num].next=h[y];h[y]=num;
    }dfs(1,0);
    printf("%d\n",f[1][0]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值