zoukankan      html  css  js  c++  java
  • [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
    ",f[1][0]);
        return 0;
    }
    
  • 相关阅读:
    ZOJ
    FZU
    FZU 2231 平行四边形数
    [转载] java的动态代理机制详解
    [转载] 解读ClassLoader
    [转载] 深入了解Java ClassLoader、Bytecode 、ASM、cglib
    MyBatis入门
    Spring入门
    Nginx入门
    Redis入门
  • 原文地址:https://www.cnblogs.com/LanrTabe/p/10326380.html
Copyright © 2011-2022 走看看