BZOJ2286 [Sdoi2011]消耗战

题目描述

在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

输入输出格式

输入格式:

 

第一行一个整数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,表示资源丰富岛屿的编号。

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
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
输出样例#1:
12
32
22

说明

【数据规模和约定】

对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1

对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)

对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)

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

 

题解:

  这个题目,首先我们考虑暴力dp,设dp[i]表示保证子树内部都合法的最小花费,那么显然,dp[i]=sum(dp[to]),dp[i]=min(dp[i],val[i]),val[i]表示i到根节点路径上的最小边权。然而看看数据范围……

  想怎么优化,显然每次询问的正真用到的关键点其实是很少的,所以我们可以打一下虚树,这样就可以把询问复杂度降为m*k*logk。于是就套一下虚树模板就可以了(然而我是现学的)。

 

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define MAXN 400100
#define ll long long
using namespace std;
ll f[MAXN],val[MAXN];
int top[MAXN],size[MAXN],dep[MAXN],son[MAXN],dfn[MAXN],fa[MAXN],b[MAXN];
int s[MAXN*2],q[MAXN];
struct edge{
    int first;
    int next;
    int to;
    int quan;
}a[MAXN*2],a2[MAXN*2];
int n,m,num=0,num2=0;

void addedge(int from,int to,int quan){
    a[++num].to=to;
    a[num].quan=quan;
    a[num].next=a[from].first;
    a[from].first=num;
}

void dfs1(int now,int F){
    fa[now]=F,size[now]=1,dep[now]=dep[F]+1;
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to,quan=a[i].quan;if(to==F) continue;
        val[to]=min((ll)quan,val[now]);
        dfs1(to,now);
        size[now]+=size[to];
        if(size[to]>size[son[now]]) son[now]=to;
    }
}

void dfs2(int now,int tp){
    top[now]=tp;dfn[now]=++num2;
    if(son[now]) dfs2(son[now],tp);
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to;
        if(to==fa[now]||to==son[now]) continue;
        dfs2(to,to);
    }
}

int Lca(int x,int y){
    int topx=top[x],topy=top[y];
    while(topx!=topy){
        if(dep[topx]<dep[topy]) swap(topx,topy),swap(x,y);
        x=fa[topx];
        topx=top[x];
    }
    if(dep[x]<dep[y]) return x;
    return y;
}

bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}

void addedge2(int from,int to,int quan){
    a2[++num].to=to;
    a2[num].quan=quan;
    a2[num].next=a2[from].first;
    a2[from].first=num;
}

void DP(int now,int FA){
    if(b[now]) f[now]=val[now];
    else f[now]=0;
    for(int i=a2[now].first;i;i=a2[i].next){
        int to=a2[i].to;
        if(to==FA||to==now) continue;
        DP(to,now);
        f[now]+=f[to];
    }
    f[now]=min(f[now],val[now]);
    b[now]=a2[now].first=0;
}

void work(){
    int tot,tp=0;
    scanf("%d",&tot);
    for(int i=1;i<=tot;i++) scanf("%d",&q[i]),b[q[i]]=1;
    sort(q+1,q+tot+1,cmp);
    num=0;
    s[++tp]=1;
    for(int i=1;i<=tot;i++){
        int lca=Lca(q[i],s[tp]);
        while(1){
            if(dep[s[tp-1]]<=dep[lca]){
                addedge2(s[tp],lca,0),addedge2(lca,s[tp],0);tp--;
                if(s[tp]!=lca) s[++tp]=lca;
                break;
            }
            addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--;
        }
        s[++tp]=q[i];
    }
    while(tp>1) {addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--;}
    DP(1,0);
    printf("%lld\n",f[1]);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z),addedge(y,x,z);
    }
    val[1]=1ll<<60;
    dfs1(1,0);dfs2(1,1);
    scanf("%d",&m);
    while(m--) work();
    return 0;
}

 

posted @ 2017-09-30 22:22  人间失格—太宰治  阅读(165)  评论(0编辑  收藏  举报