zoukankan      html  css  js  c++  java
  • 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
    ",dp(1));
        }
        return 0;
    }

     

  • 相关阅读:
    MySQL——字符串拆分(含分隔符的字符串截取)
    一列数据组合成字符串并用逗号隔开
    web最大化,最小化文字错位的问题
    Esxi虚拟机安装Ros+Openwrt软路由双系统简单分享(踩到的坑,很大的坑)
    跟着老司机免费申请一个域名去!
    logging模块
    os.rename 和os.replace
    装饰器详解
    python if not
    globals和locals的区别
  • 原文地址:https://www.cnblogs.com/Never-mind/p/10184421.html
Copyright © 2011-2022 走看看