BZOJ2286:[SDOI2011]消耗战——题解

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

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

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

UPD 18/5/3:我终于懂虚树啦!以及美化代码。

显然是虚树啊……然而我还是不懂虚树怎么建的……

但是dp十分的简单,就是在虚树上dp[i]表示从i以下的资源节点都到不了i的最小费用。

设u为v的儿子,则:

当u有资源的时候,显然是从v到u的路径中挑一条最小费用的路径割掉即可。

当u没有资源的时候,要么将u往下的资源节点和u断开联系(即将dp[v]+=dp[u]),要么选择从v到u的路径中挑一条最小费用的路径割掉。

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<stack>
using namespace std;
typedef long long ll;
const int N=250005,B=18;
const int INF=0x7fffffff;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int w,to,nxt;
}e[2*N];
int n,m,cnt,head[N],dep[N];
int anc[N][B+4],val[N][B+4];
int aux[N],pos[N],stk[N],tot;
int fa_aux[N],tmp[N],num,top;
bool vis[N];
ll dp[N];
inline bool cmp(int a,int b){return pos[a]<pos[b];} 
inline void add(int u,int v,int w){
    e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u){
    pos[u]=++tot;dep[u]=dep[anc[u][0]]+1;
    for(int i=1;i<=B;i++){
        anc[u][i]=anc[anc[u][i-1]][i-1];
        val[u][i]=min(val[u][i-1],val[anc[u][i-1]][i-1]);
    }
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to,w=e[i].w;
        if(anc[u][0]!=v){
            anc[v][0]=u;val[v][0]=w;
            dfs(v);
        }
    }
}
inline int LCA(int i,int j){
    if(dep[i]<dep[j])swap(i,j);
    for(int k=B;k>=0;--k)
        if(dep[anc[i][k]]>=dep[j])i=anc[i][k];
    if(i==j)return i;
    for(int k=B;k>=0;--k)
        if(anc[i][k]!=anc[j][k])
            i=anc[i][k],j=anc[j][k];
    return anc[i][0];
}
void build(int t){
    sort(aux+1,aux+t+1,cmp);
    num=t;stk[top=0]=0;
    for(int i=1;i<=t;i++){
        int u=aux[i];
        if(!top)fa_aux[u]=0,stk[++top]=u;
        else{
            int lca=LCA(u,stk[top]);
            while(dep[stk[top]]>dep[lca]){
                if(dep[stk[top-1]]<=dep[lca])
                    fa_aux[stk[top]]=lca;
                top--;
            }
            if(stk[top]!=lca){
                fa_aux[lca]=stk[top];
                stk[++top]=lca;
                aux[++num]=lca;
            }
            fa_aux[u]=lca;
            stk[++top]=u;
        }
    }
    sort(aux+1,aux+num+1,cmp);
}
inline ll cut(int u,int v){
    int res=INF;
    for(int i=B;i>=0;i--)
        if(dep[anc[u][i]]>=dep[v]){
            res=min(res,val[u][i]);
            u=anc[u][i];
        }
    return res;
}
void solve(){
    for(int i=1;i<=num;i++)dp[aux[i]]=0;
    for(int i=num;i>=1;i--){
        int u=aux[i],v=fa_aux[u];
        if(!vis[u])
            dp[v]+=min(dp[u],cut(u,v));
        else dp[v]+=cut(u,v);
    }
}
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w);add(v,u,w);
    }
    dfs(1);
    m=read();
    while(m--){
        int t=read();
        for(int i=1;i<=t;i++){
            int u=read();
            aux[i]=tmp[i]=u;vis[u]=1;
        }
        aux[++t]=1;
        build(t);solve();
        printf("%lld\n",dp[1]);
        for(int i=1;i<=t;i++)vis[tmp[i]]=0;
    }
}

 

posted @ 2018-01-12 17:16  luyouqi233  阅读(241)  评论(1编辑  收藏  举报