• bzoj千题计划272:bzoj4557: [JLoi2016]侦察守卫


    http://www.lydsy.com/JudgeOnline/problem.php?id=4557

    假设当前到了x的子树,现在是合并 x的第k个子树

    f[x][j] 表示x的前k-1个子树该覆盖的完全覆盖,而且还能向上覆盖j层的最小代价

    这个向上是针对x来说的,即可以向x的祖先方向再覆盖j层

    对于第k个子树的意义就是,兄弟子树放置的守卫可以帮x的第k个子树覆盖前j层(第1层为x的子节点)

    那么相应的就要有一个状态来表示这个 可以让兄弟子树 帮忙覆盖 的前j层

    g[x][j] 表示还需要覆盖x的前k个子树中的前j层,且第j层以下该覆盖的完全覆盖(第1层为x)的最小代价

    状态转移:

    设x的第k个子节点为y

    向x的上方覆盖j层,只需要x的子节点中有一个子节点z能向上覆盖j+1层 即可

    所以f的转移有两种:z是前k-1个子节点中的,z是第k个子节点

    f[x][j]=min(f[x][j]+g[y][j] ,f[y][j+1]+g[x][j+1])

    g[x][j]+=g[y][j-1]

    但是有可能x 再向上恰好覆盖j层的代价要小于再向上恰好覆盖j-1层的代价

    即覆盖的更多代价反而要小

    所以将f的状态定义改为 向上覆盖至少j层的最小代价

    同理,g的状态定义改为还需要覆盖至多j层的最小代价

    对f[x][]做一个后缀最小值,g[x][]做一个前缀最小值

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 500001
    
    int d;
    int w[N];
    bool use[N];
    
    int front[N],to[N<<1],nxt[N<<1],tot;
    
    int f[N][22],g[N][22];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar(); 
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void add(int u,int v)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
        to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
    }
    
    void dfs(int x,int fa)
    {
        for(int i=1;i<=d;++i) f[x][i]=w[x];
        if(use[x]) g[x][0]=f[x][0]=w[x];
        f[x][d+1]=1e9;
        int t;
        for(int i=front[x];i;i=nxt[i])
        {
            t=to[i];
            if(t!=fa)
            {
                dfs(t,x);
                for(int j=0;j<=d;++j) f[x][j]=min(f[x][j]+g[t][j],f[t][j+1]+g[x][j+1]);
                for(int j=d;j>=0;--j) f[x][j]=min(f[x][j],f[x][j+1]);
                g[x][0]=f[x][0];
                for(int j=1;j<=d;++j) g[x][j]+=g[t][j-1];
                for(int j=1;j<=d;++j) g[x][j]=min(g[x][j],g[x][j-1]);
            }
        }
    }
    
    int main()
    {
        int n,m,x; 
        read(n); read(d);
        for(int i=1;i<=n;++i) read(w[i]);
        read(m);
        while(m--) { read(x); use[x]=true; }
        int u,v;
        for(int i=1;i<n;++i)
        {
            read(u); read(v);
            add(u,v);
        }
        dfs(1,0);
        printf("%d",g[1][0]);
        return 0;
    }
  • 相关阅读:
    <JavaScript> 组合继承
    <JavaScript> 稳妥构造函数模式与工厂模式的区别
    <JavaScript> call()、apply()、bind() 的用法
    <JavaScript>可枚举属性与不可枚举属性
    <JavaScript>闭包(closure)
    在MongoDB中实现聚合函数
    (转)如何入门 Python 爬虫
    Python爬虫实战四之抓取淘宝MM照片
    转载:十年驾车经验总结:活着,才是硬道理
    设计模式之单例模式的七种写法
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8544819.html
Copyright © 2020-2023  润新知