BZOJ2286: [Sdoi2011]消耗战

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

考虑暴力做法,对于每次询问都做一次DP,复杂度O(nm)

引入新姿势:虚树,考虑sigma(ki)很小,建出虚树:只有用到的点和他们的LCA

然后虚树上DP即可

代码如下:

//MT_LI
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f*=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct node{
    int x,y,d,next;
}a[510000],e[510000];int elen,len,last[510000],first[510000];
void ins(int x,int y,int d)
{
    len++;
    a[len].x=x;a[len].y=y;a[len].d=d;
    a[len].next=last[x];last[x]=len;
}
void add(int x,int y)
{
    elen++;
    e[elen].x=x;e[elen].y=y;
    e[elen].next=first[x];first[x]=elen;
}
typedef long long ll;
ll v[510000];
int id[510000],cnt;
int bin[25],f[510000][25],dep[510000];
void dfs(int x,int fa)
{
    id[x]=++cnt;
    dep[x]=dep[fa]+1;f[x][0]=fa;
    for(int i=1;bin[i]<=dep[x];i++)f[x][i]=f[f[x][i-1]][i-1];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fa)
        {
            v[y]=min(v[x],(ll)a[k].d);
            dfs(y,x);
        }
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[x]-bin[i]>=dep[y])
            x=f[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
bool cmp(int x,int y){return id[x]<id[y];}
int k,p[510000],top,sta[510000];
void build()
{
    sort(p+1,p+k+1,cmp);
    int tp=1;
    for(int i=2;i<=k;i++)if(LCA(p[tp],p[i])!=p[tp])p[++tp]=p[i];
    k=tp;
    top=0;sta[++top]=1;
    for(int i=1;i<=k;i++)
    {
        int lca=LCA(p[i],sta[top]);
        while(1)
        {
            if(dep[lca]>=dep[sta[top-1]])
            {
                if(lca!=sta[top])add(lca,sta[top]);
                top--;
                if(lca!=sta[top])sta[++top]=lca; 
                break;
            }
            add(sta[top-1],sta[top]);top--;
        }
        if(p[i]!=sta[top])sta[++top]=p[i];
    }
    top--;
    while(top){add(sta[top],sta[top+1]);top--;}
}
ll dp[510000];
void treedp(int x)
{
    ll ans=0;dp[x]=v[x];
    for(int k=first[x];k;k=e[k].next)
    {
        int y=e[k].y;
        treedp(y);ans+=dp[y];
    }
    first[x]=0;
    if(ans)dp[x]=min(dp[x],ans);
}
int main()
{
    bin[0]=1;for(int i=1;i<=24;i++)bin[i]=bin[i-1]<<1;
    int n=read();
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        int x,y,d;
        x=read();y=read();d=read();
        ins(x,y,d);ins(y,x,d);
    }
    cnt=0;dep[1]=1;v[1]=1ll<<60;dfs(1,0);
    int m=read();
    while(m--)
    {
        k=read();
        for(int i=1;i<=k;i++)p[i]=read();
        elen=0;build();treedp(1);
        printf("%lld\n",dp[1]);
    }
    return 0;
}

 

posted @ 2018-12-04 13:41  MT_LI  阅读(164)  评论(0编辑  收藏  举报