BZOJ 4557: [JLoi2016]侦察守卫 树形DP

状态还比较自然. 

令 $f[x][j]$ 表示 $x$ 的子树全被控制,且多出来 $j$ 层. 

令 $g[x][j]$ 表示还需要 $j$ 层才能控制 $x$ 所有子树.     

转移 $f[x][j]$ 的时候有两种情况:之前已经控制:$f[x][j]+g[y][j]$,之前没控制:$g[x][j+1]+f[y][j+1]$.   

注意一下转移边界就好了. 

#include <cstdio> 
#include <string> 
#include <cstring> 
#include <algorithm>    
#define N 500002   
#define inf 1000000000   
using namespace std;   
void setIO(string s) 
{
    string in=s+".in"; 
    string out=s+".out"; 
    freopen(in.c_str(),"r",stdin); 
    // freopen(out.c_str(),"w",stdout); 
}    
int D,n,edges;
int f[N][21],g[N][21],v[N],vis[N],hd[N],to[N<<1],nex[N<<1];   
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;  
}
void dfs(int x,int ff) 
{   
    if(vis[x]) f[x][0]=g[x][0]=v[x];    
    int i,j; 
    for(i=1;i<=D;++i)   f[x][i]=v[x];  
    f[x][D+1]=inf;       
    for(i=hd[x];i;i=nex[i]) if(to[i]!=ff) 
    {
        int y=to[i];          
        dfs(y,x);                 
        for(j=0;j<=D;++j) 
            f[x][j]=min(f[x][j]+g[y][j],f[y][j+1]+g[x][j+1]);        
        for(j=D;j>=0;--j) f[x][j]=min(f[x][j],f[x][j+1]);    
        g[x][0]=f[x][0];
        for(j=1;j<=D;++j) g[x][j]+=g[y][j-1];    
        for(j=1;j<=D;++j) g[x][j]=min(g[x][j],g[x][j-1]); 
    }
}
int main() 
{ 
    // setIO("input");      
    int i,j,m; 
    scanf("%d%d",&n,&D);           
    for(i=1;i<=n;++i) scanf("%d",&v[i]);   
    scanf("%d",&m); 
    for(i=1;i<=m;++i) 
    {
        int x; 
        scanf("%d",&x),vis[x]=1; 
    }          
    for(i=1;i<n;++i) 
    {
        int x,y;                  
        scanf("%d%d",&x,&y),add(x,y),add(y,x); 
    }    
    dfs(1,0);    
    printf("%d\n",g[1][0]);  
    return 0; 
}

  

posted @ 2020-01-02 18:09  EM-LGH  阅读(122)  评论(0编辑  收藏  举报