[BZOJ4557/LOJ2024/Luogu3267][GZOI2016/JLOI2016/SHOI2016]侦察守卫

题目链接:

4557:[JLoi2016]侦察守卫-BZOJ

#2024.「JLOI/SHOI2016」侦查守卫-Libre OJ

P3267[JLOI2016/SHOI2016]侦察守卫-Luogu

首先对于这数据范围。。一看就知道是个\(O(nd)\)的树形\(DP\)了。

那么是\(O(n)\)的状态\(O(d)\)转移,还是\(O(nd)\)状态数\(O(1)\)转移呢?

如果是前者,不说\(O(d)\),直接没法转移啊\(qwq\)

那么就只能选另一种方法了。

\(f[i][j]\)表示在以\(i\)为根的子树中有\(j\)层没有监视(包括\(i\))时的最小代价。

\(g[i][j]\)表示在以\(i\)为根的子树中监视完,还能向上监视\(j\)层(包括\(i\))的最小代价。

那么转移方程就很好写了,详细见代码。

代码:

#include <cstdio>
#include <cctype>

inline int Min(const int a,const int b){return a<b?a:b;}
inline int Max(const int a,const int b){return a>b?a:b;}
inline int Getint()
{
    register int x=0,c;
    while(!isdigit(c=getchar()));
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    return x;
}

int n,d,w[500005],m;
int Head[500005],Next[1000005],To[1000005],En;
int f[500005][22],g[500005][22];
bool Vis[500005];

inline void Add(const int x,const int y)
{
    Next[++En]=Head[x];
    To[Head[x]=En]=y;
}

void DP(int x,int Pre)
{
    if(Vis[x])f[x][0]=g[x][0]=w[x];
    //有"B"神,那么当只监视这层时必须放守卫。
    for(int i=1;i<=d;++i)g[x][i]=w[x];
    //在这里放是其中一种方案
    g[x][d+1]=0x3f3f3f3f;
    //避免不合法情况
    for(int i=Head[x],y;i;i=Next[i])
        if((y=To[i])!=Pre)
        {
            DP(y,x);//遍历子树
            for(int j=d;j>=0;--j)
            {
                g[x][j]=Min(g[x][j]+f[y][j],g[y][j+1]+f[x][j+1]);
                g[x][j]=Min(g[x][j],g[x][j+1]);
            }
            //将y和之前的子树结果合并,或者将x,y交换后合并
            //第一种决策(g[x][j]+f[y][j]):
            //因为g[x][j]已经能向上监视j层,向下也可以(守卫在子树内)
            //那么子树y中可以有j层不用管
            //第二种决策(g[y][j+1]+f[x][j+1])同理,注意要倒序处理,防止覆盖(类似背包?)
            //至于最后一个(g[x][j+1]),如果范围更大还便宜,岂不美哉?
            f[x][0]=g[x][0];//如果j=0,那么f和g意义相同。
            for(int j=1;j<=d+1;++j)
                f[x][j]=Min(f[x][j]+f[y][j-1],f[x][j-1]);
            //第一种是直接合并,那么另一个就是取最优了。
        }
}

int main()
{
    n=Getint(),d=Getint();
    for(int i=1;i<=n;++i)w[i]=Getint();
    m=Getint();
    for(int i=1;i<=m;++i)Vis[Getint()]=true;
    for(int x,y,i=1;i<n;++i)
    {
        x=Getint(),y=Getint();
        Add(x,y),Add(y,x);
    }
    DP(1,0);
    printf("%d\n",f[1][0]);
    return 0;
}
posted @ 2019-01-27 14:27  LanrTabe  阅读(389)  评论(0编辑  收藏  举报