BZOJ2286: [Sdoi2011]消耗战

【传送门:BZOJ2286


简要题意:

  给出一棵根为1的树,树边有边权

  有m个询问,每个询问输入k个点的编号,求出在使得这k个点不能与根节点连通的情况下,总共要删掉的边权的总和最小,求出最小总边权


题解:

  假如只有一个询问,那么我们可以直接dp

  对于树上的点x,转移分为两种情况

  1.断开自己与父亲的联系,代价为从根到该节点的最小值

  2.不考虑该节点(前提是该节点不是询问点),把子树内的所有询问点都断开的代价

  但是如果有m个询问的话,逐个逐个DP的复杂度显然O(mn)会爆炸

  有一个地方可以作为突破口的就是Σk<=500000

  就要用虚树来做了,详细请左转

  而且这题在构造的时候要注意,有两个询问点x,y,y在以x为根的子树内,就不把y建到虚树中,因为我们只要分割了x,y自然就被分割了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#define Maxn 310000
using namespace std;
typedef long long LL;
struct node{int x,y,next;LL d;}a[Maxn*2];int len,last[Maxn];
void ins(int x,int y,LL d){a[++len]=(node){x,y,last[x],d};last[x]=len;}
vector<int> v[Maxn];
int dfn[Maxn],id,dep[Maxn];LL mn[Maxn];
int f[Maxn][21];
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=(1<<i)) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
    {
        if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i])
        {
            x=f[x][i];y=f[y][i];
        }
    }
    return f[x][0];
}
void dfs(int x,int fa)
{
    dfn[x]=++id;
    for(int i=1;dep[x]>=(1<<i);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==f[x][0]) continue;
        mn[y]=min(mn[x],a[k].d);
        f[y][0]=x;dep[y]=dep[x]+1;
        dfs(y,x);
    }
}
int p[Maxn];
bool cmp(int x,int y){return dfn[x]<dfn[y];}
int sta[Maxn],tp;
void add(int x)
{
    if(tp==1){sta[++tp]=x;return ;}
    int lca=LCA(x,sta[tp]);
    if(lca==sta[tp]) return ;//因为阻隔了上面的点就能阻隔下面的点 
    while(tp>1&&dfn[sta[tp-1]]>=dfn[lca]) v[sta[tp-1]].push_back(sta[tp]),tp--;
    if(lca!=sta[tp]) v[lca].push_back(sta[tp]),sta[tp]=lca;
    sta[++tp]=x;
}
LL dp(int x)
{
    if(v[x].size()==0) return mn[x];
    LL d=0;
    for(int i=0;i<v[x].size();i++)
    {
        int y=v[x][i];
        d+=dp(y);
    }
    v[x].clear();
    return min(d,mn[x]);
}
int main()
{
    int n;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        int x,y,d;
        scanf("%d%d%d",&x,&y,&d);
        ins(x,y,d);ins(y,x,d);
    }
    memset(mn,63,sizeof(mn));
    id=dep[1]=0;dfs(1,0);
    int m;
    scanf("%d",&m);
    while(m--)
    {
        int k;
        scanf("%d",&k);
        for(int i=1;i<=k;i++) scanf("%d",&p[i]);
        sort(p+1,p+k+1,cmp);
        tp=0;sta[++tp]=1;
        for(int i=1;i<=k;i++) add(p[i]);
        while(tp!=0) v[sta[tp-1]].push_back(sta[tp]),tp--;
        printf("%lld\n",dp(1));
    }
    return 0;
}

 

posted @ 2018-12-27 13:20  Star_Feel  阅读(86)  评论(0编辑  收藏