zoukankan      html  css  js  c++  java
  • [P2495][SDOI2011]消耗战——虚树

    [SDOI2011]消耗战

            题意:一棵树上给定点集,求到根的最小割。

            每次询问只和给定的点集以及它们的LCA有关系,那么把这些点拿出来建立一棵树。再进行树DP。具体操作把所有询问点按照dfn排序,再用一个栈进行维护建树。弹栈的时候并不是每个点都和栈中第二个元素连边,有可能最后一个弹栈点和新的LCA连边。DP转移时需要查询树链的最小边,这里用倍增做。

    #include<bits/stdc++.h>
    #define cmin(a,b) (a>b?a=b:a)
    using namespace std;
    typedef long long ll;
    const int N=5e5+10;
    const int M=2.5e5+10;
    int head[N],ver[2*M],edge[2*M],nex[2*M],tot=1;
    inline void add(int x,int y,int z) {
        ver[++tot]=y,edge[tot]=z,nex[tot]=head[x],head[x]=tot;
    }
    
    int d[N],f[N][25],Min[N][25],dfn[N],num,_t,n;
    void dfs(int x){//倍增预处理
        dfn[x]=++num;
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i];if(y==f[x][0])continue;
            d[y]=d[x]+1;f[y][0]=x;Min[y][0]=edge[i];
            for(int i=1;i<=_t;++i){
                f[y][i]=f[f[y][i-1]][i-1];
                Min[y][i]=min(Min[y][i-1],Min[f[y][i-1]][i-1]);
            }
            dfs(y);
        }
    }
    int lca(int x,int y) {
        if(d[x]<d[y])swap(x,y);
        if(d[x]>d[y])
            for(int i=_t; i>=0; --i)
                if(d[f[x][i]]>=d[y])
                    x=f[x][i];
        if(x==y)return x;
        for(int i=_t; i>=0; --i)
            if(f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int minEdge(int x,int y){//x-y最大的边
        int res=2e9;
        if(d[x]<d[y])swap(x,y);
        if(d[x]>d[y])
            for(int i=_t; i>=0; --i)
                if(d[f[x][i]]>=d[y])
                    cmin(res,Min[x][i]),x=f[x][i];
        if(x==y)return res;
        for(int i=_t; i>=0; --i)
            if(f[x][i]!=f[y][i]){
                cmin(res,Min[x][i]);cmin(res,Min[y][i]);
                x=f[x][i];y=f[y][i];
            }
        cmin(res,Min[x][0]);cmin(res,Min[y][0]);
        return res;
    }
    
    bool cmp(int x,int y){return dfn[x]<dfn[y];}
    int stk[N],top,fa[N],a[N],in_deg[N],cntq;
    void build(){//建立虚树
        sort(a,a+cntq,cmp);//按dfn排序
        stk[top=1]=1;
        for(int i=0;i<cntq;++i){
            int LCA=lca(a[i],stk[top]);
            while(dfn[stk[top-1]]>=dfn[LCA]){
                fa[stk[top]]=stk[top-1],--top;
                ++in_deg[stk[top]];
            }
            if(dfn[stk[top]]>dfn[LCA])fa[stk[top--]]=LCA,++in_deg[LCA];
            if(stk[top]!=LCA)stk[++top]=LCA;
            stk[++top]=a[i];
        }
        while(top>1){
            fa[stk[top]]=stk[top-1],--top;
            ++in_deg[stk[top]];
        }
    }
    
    ll dp[N];
    int isq[N],clear[N],cnt;//isq:询问点,clear:等待清空
    ll solve(){
        queue<int> q;
        cnt=0;
        for(int i=0;i<cntq;++i)if(in_deg[a[i]]==0)
            q.push(a[i]);
        while(!q.empty()){
            int x=q.front();q.pop();
            clear[cnt++]=x;
            if(x==1)break;
            if(isq[x])dp[fa[x]]+=minEdge(x,fa[x]);
            else dp[fa[x]]+=min(dp[x],(ll)minEdge(x,fa[x]));
            if(--in_deg[fa[x]]==0)q.push(fa[x]);
        }
        ll ans=dp[1];
        for(int i=0;i<cnt;++i)dp[clear[i]]=0;//清空dp数组
        for(int i=0;i<cntq;++i)isq[a[i]]=0;//清空询问点标记
        return ans;
    }
    int main(){
        int m,x,y,z;
        scanf("%d",&n);
        for(int i=1;i<n;++i){
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);add(y,x,z);
        }
        _t=log(n+0.5)/log(2);//init
        d[1]=1;//init
        dfs(1);
        scanf("%d",&m);
        while(m--){
            scanf("%d",&cntq);
            for(int i=0;i<cntq;++i)scanf("%d",a+i),isq[a[i]]=1;
            build();
            printf("%lld
    ",solve());
        }
        return 0;
    }
    View Code
  • 相关阅读:
    类加载器ClassLoader
    JAVA分别获取日期中的年、月、日
    sql 安全问题
    马尔科夫链
    触发器、锁、事务和事务控制
    索引、视图、存储过程、函数、游标
    字符集
    数据类型选择
    存储引擎
    错误:too many indices for array
  • 原文地址:https://www.cnblogs.com/zpengst/p/12799993.html
Copyright © 2011-2022 走看看