[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\n",solve());
    }
    return 0;
}
View Code

 

posted @ 2020-04-29 09:32  _ZPENG  阅读(141)  评论(0编辑  收藏  举报