洛谷 P2495 [SDOI2011]消耗战 虚树

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82144210

题目描述

在一场战争中,战场由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 <= k i <= n 1
对于 20 % 的数据,
2 <= n <= 100 , 1 <= m <= 100 , 1 <= k i <= m i n ( 10 , n 1 )
对于 40 % 的数据,
2 <= n <= 1000 , m >= 1 , k i <= 500000 , 1 <= k i <= m i n ( 15 , n 1 )
对于 100 % 的数据,
2 <= n <= 250000 , m >= 1 , k i <= 500000 , 1 <= k i <= n 1

分析:
一个很浅显的树形dp,设 f [ i ] 为断掉 i 于其子树内所有关键点的代价,显然

f [ i ] = j s o n [ i ] m i n ( w ( i , j ) , f [ j ] )

特殊的,当 i 为关键点时, f [ i ] = i n f
因为 k i 比较小,所以我们不一定每一次询问都要跑整棵树一遍。我们可以对原树建一棵虚树,虚树是只保留所有关键点和他们的 l c a 的树。
我们维护一个栈,栈中元素表示一条从当前点到根的链。我们先对关键点排序,我们插入一个点时,如果这个点时栈顶元素的儿子,直接加入;否则不断弹出栈顶,知道这个元素是当前元素的父亲。
注意:虚树上的边权为原路径上边权的最小值。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define LL long long

const int maxn=250007;
const LL inf=1e16;

using namespace std;

int n,m,x,y,w,cnt,tot,top;
int ls[maxn],dfn[maxn],dep[maxn],a[maxn],pre[maxn];
int f[maxn][20],h[maxn][20];
int q[maxn];
LL ans[maxn];

struct edge{
    int y,w,next;
}g[maxn*2];

struct node{
    int x,key;
}b[maxn];

void add(int x,int y,int w)
{
    g[++cnt]=(edge){y,w,ls[x]};
    ls[x]=cnt;
}

void dfs(int x,int fa)
{
    dfn[x]=++cnt;
    f[x][0]=fa;
    dep[x]=dep[fa]+1;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        h[y][0]=g[i].w;
        dfs(y,x);
    }
}

int lca(int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    int d=dep[y]-dep[x],k=19,t=1<<k;
    while (d)
    {
        if (d>=t)
        {
            d-=t;
            y=f[y][k];
        }
        t/=2,k--;
    }
    if (x==y) return x;
    k=19;
    while (k>=0)
    {
        if (f[x][k]!=f[y][k])
        {
            x=f[x][k];
            y=f[y][k];
        }
        k--;
    }
    return f[x][0];
}

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

bool cmp1(node x,node y)
{
    return dfn[x.x]<dfn[y.x];
}

LL getmin(int x,int y)
{
    int d=dep[y]-dep[x],k=19,t=1<<k;
    LL minn=inf;
    while (d)
    {
        if (d>=t)
        {
            d-=t;
            minn=min(minn,(LL)h[y][k]);
            y=f[y][k];
        }
        t/=2,k--;
    }
    return minn;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);
        add(y,x,w);
    }
    cnt=0;
    dfs(1,0);
    for (int j=1;j<20;j++)
    {
        for (int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            h[i][j]=min(h[i][j-1],h[f[i][j-1]][j-1]);
        }
    }
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {       
        scanf("%d",&tot);
        for (int j=1;j<=tot;j++) scanf("%d",&a[j]);
        a[++tot]=1;
        sort(a+1,a+tot+1,cmp);
        top=0,cnt=0;
        q[++top]=a[1];
        b[++cnt]=(node){a[1],0};
        for (int j=2;j<=tot;j++)
        {
            b[++cnt]=(node){a[j],1};
            int d=lca(q[top],a[j]);
            if (d==q[top]) q[++top]=a[j];
            else
            {
                while (dep[q[top-1]]>=dep[d])
                {
                    pre[q[top]]=q[top-1];
                    top--;
                }
                if (q[top]!=d)
                {
                    pre[q[top]]=d;
                    q[top]=d;
                    b[++cnt]=(node){d,0};
                }
                q[++top]=a[j];
            }
        }
        while (top>1) pre[q[top]]=q[top-1],top--;
        sort(b+1,b+cnt+1,cmp1);
        for (int i=1;i<=cnt;i++) ans[b[i].x]=0;
        for (int i=cnt;i>0;i--)
        {
            int x=b[i].x;
            if (b[i].key) ans[x]=inf;
            ans[pre[x]]+=min(ans[x],(LL)getmin(pre[x],x));
        }
        printf("%lld\n",ans[1]);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82144210