• [BZOJ2286][Sdoi2011]消耗战(虚树上DP)


    2286: [Sdoi2011]消耗战

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 6457  Solved: 2533
    [Submit][Status][Discuss]

    Description

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    Input

    第一行一个整数n,代表岛屿数量。

    接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。

    第n+1行,一个整数m,代表敌方机器能使用的次数。

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    Output

    输出有m行,分别代表每次任务的最小代价。

    Sample Input

    10
    1 5 13
    1 9 6
    2 1 19
    2 4 8
    2 3 91
    5 6 8
    7 5 4
    7 8 31
    10 7 9
    3
    2 10 6
    4 5 7 8 3
    3 9 4 6

    Sample Output

    12
    32
    22

    HINT

     对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

    Source

    Stage2 day2

    Solution  

      一道虚树上dp的板子题。

      设d[x]为根到x路径上的最小边,那么对于标记点,显然只能决策d[x],而非标记点可以决策min(d[x],sigma(d[y])),y为x的所有儿子。

      有两种建虚树的方法,第一种是网上的常规建法,第二种是从yyb哪儿学到的,要更简单一些。两种我都写了的。

      注意下第一种方法的insert函数里的注释,因为我们这样特殊处理了所以第一种方法不用打标记,dp时也不用限制决策;

      而第二种方法是把整棵虚树都建出来了的,因此必须打标记,而且要随时注意回溯时清空标记(不能直接memset就不用我说了吧)。

    Code 1

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=250005,INF=0x3f3f3f3f;
    inline int read(){
        int x=0,w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    struct edge{
        int v,w,last;
    }e[N<<1];
    int tot,tail[N];
    inline void add(int x,int y,int z){
        e[++tot]=(edge){y,z,tail[x]};
        tail[x]=tot;
    }
    int idx,dfn[N],dep[N],d[N],f[N][20];
    int dfs(int x,int pre){
        dfn[x]=++idx;dep[x]=dep[pre]+1;f[x][0]=pre;
        for(int i=1;;++i)
            if(f[x][i-1]) f[x][i]=f[f[x][i-1]][i-1];
            else break;
        for(int p=tail[x];p;p=e[p].last){
            int &v=e[p].v,&w=e[p].w;
            if(v==pre) continue;
            d[v]=min(d[x],w);
            dfs(v,x);
        }
    }
    int LCA(int x,int y){
        if(dep[x]<dep[y]) x^=y^=x^=y;
        for(int i=19;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
        for(int i=19;i>=0;--i) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int n,m,k,tp,st[N],a[N];
    void insert(int x){
        if(tp==1){st[++tp]=x;return ;}
        int lca=LCA(x,st[tp]);
        if(lca==st[tp]){
            /*任意时刻如果lca=st[tp]的话那么lca一定是标记点(lca=1除外),既然lca和x都是标记点,
              那么x就不用管了呗,相当于把x的决策合并到lca上去了。*/ 
            return ;
        }
        while(tp>1&&dfn[st[tp-1]]>=dfn[lca]) add(st[tp-1],st[tp],0),tp--;
        if(st[tp]^lca) add(lca,st[tp],0),st[tp]=lca;
        st[++tp]=x;
    }
    LL dp(int x){
        if(!tail[x]) return d[x];
        LL sum=0;
        for(int p=tail[x];p;p=e[p].last)
            sum+=dp(e[p].v);
        tail[x]=0;
        if(x==1) return sum;
        else return min(sum,1ll*d[x]);
    }
    bool cmp(int x,int y){return dfn[x]<dfn[y];}
    int main(){
        n=read();
        for(int i=1,x,y,z;i<n;++i){
            x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }
        d[1]=INF;
        dfs(1,0); 
        m=read();   
        tot=0;memset(tail,0,sizeof tail);    
        st[tp=1]=1;
        while(m--){      
            tot=0;
               k=read();
            for(int i=1;i<=k;++i) a[i]=read(); 
            sort(a+1,a+k+1,cmp);
            for(int i=1;i<=k;++i) insert(a[i]);
            while(tp>1) add(st[tp-1],st[tp],0),--tp;
            printf("%lld
    ",dp(1));
        }
        return 0;
    }
    BZOJ2286

    Code 2

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=250005,INF=0x3f3f3f3f;
    inline int read(){
        int x=0,w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    struct edge{
        int v,w,last;
    }e[N<<1];
    int tot,tail[N];
    inline void add(int x,int y,int z){
        e[++tot]=(edge){y,z,tail[x]};
        tail[x]=tot;
    }
    int idx,dfn[N],dep[N],d[N],low[N],f[N][20];
    int dfs(int x,int pre){
        dfn[x]=++idx;dep[x]=dep[pre]+1;f[x][0]=pre;
        for(int i=1;;++i)
            if(f[x][i-1]) f[x][i]=f[f[x][i-1]][i-1];
            else break;
        for(int p=tail[x];p;p=e[p].last){
            int &v=e[p].v,&w=e[p].w;
            if(v==pre) continue;
            d[v]=min(d[x],w);
            dfs(v,x);
        }
        low[x]=idx;
    }
    int LCA(int x,int y){
        if(dep[x]<dep[y]) x^=y^=x^=y;
        for(int i=19;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
        for(int i=19;i>=0;--i) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    bool tag[N];
    int n,m,k,st[N<<1],a[N<<1];
    LL dp(int x){
        if(!tail[x]) {tag[x]=0;return d[x];}
        LL sum=0;
        for(int p=tail[x];p;p=e[p].last)
            sum+=dp(e[p].v);
        tail[x]=0;
        if(x==1) return sum;
        if(tag[x]) {tag[x]=0;return d[x];}
        return min(sum,1ll*d[x]);
    }
    bool cmp(int x,int y){return dfn[x]<dfn[y];}
    int main(){
        n=read();
        for(int i=1,x,y,z;i<n;++i){
            x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }
        d[1]=INF;
        dfs(1,0); 
        m=read();   
        tot=0;memset(tail,0,sizeof tail);    
        while(m--){      
            tot=0;
               k=read();
            for(int i=1;i<=k;++i) a[i]=read(),tag[a[i]]=true; 
            a[++k]=1; //手动添加1号节点 
            sort(a+1,a+k+1,cmp);
            for(int i=k;i>1;--i)
                a[++k]=LCA(a[i],a[i-1]);
            sort(a+1,a+k+1,cmp);
            k=unique(a+1,a+k+1)-a-1;
            for(int i=1,tp=0;i<=k;++i){
                while(tp&&low[st[tp]]<dfn[a[i]]) --tp;
                if(tp) add(st[tp],a[i],0);
                st[++tp]=a[i];
            }
            printf("%lld
    ",dp(1));
        }
        return 0;
    }
    BZOJ2286
  • 相关阅读:
    深度优先搜索
    哈希算法
    双指针问题
    基本概念
    Ionic JPush极光推送二
    一条sql获取每个类别最新的一条记录
    Ionic App 更新插件cordova-plugin-app-version
    Ionic跳转到外网地址
    Ionic cordova-plugin-splashscreen
    Web API 上传下载文件
  • 原文地址:https://www.cnblogs.com/gosick/p/11254786.html
Copyright © 2020-2023  润新知