最小割(zjoi2011,bzoj2229)(最小割树)

小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点\(s,t\)不在同一个部分中,则称这个划分是关于\(s,t\)的割。 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而\(s,t\)的最小割指的是在关于\(s,t\)的割中容量最小的割” 现给定一张无向图,小白有若干个形如“图中有多少对点它们的最小割的容量不超过\(x\)呢”的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。

Input

输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数\(n,m\),表示图的点数和边数。 下面\(m\)行,每行3个正整数\(u,v,c(1<=u,v<=n,0<=c<=10^6)\),表示有一条权为\(c\)的无向边\((u,v)\) 接下来一行,包含一个整数\(q\),表示询问的个数 下面\(q\)行,每行一个整数\(x\),其含义同题目描述。

Output

对于每组测试数据,输出应包括\(q\)行,第\(i\)行表示第\(i\)个问题的答案。对于点对\((p,q)\)\((q,p)\),只统计一次(见样例)。

两组测试数据之间用空行隔开。

Sample Input

1
5 0
1
0

Sample Output

10

Hint

对于100%的数据 \(T<=10,n<=150,m<=3000,q<=30\)\(x\)在32位有符号整数类型范围内。
图中两个点之间可能有多条边

题意:

中文题面,不解释

题解:

最小割树求出所有的最小割,放入一个数组里面,然后对于么一个询问二分就行了。
有一个坑点,就是每组数据处理完之后要额外输出一个空行。

#include<bits/stdc++.h>
#define re register 
using namespace std;
const int N=300,M=10010;
const long long inf=1ll<<60;
int n,m,a[N];
long long ans[N][N];
int head[N],nxt[M],bian[M];
long long zhi[M];
int tot;
void init(){
    tot=1;
    memset(head,0,sizeof head);
}
void add(int x,int y,long long z){
    tot++;bian[tot]=y;zhi[tot]=z;nxt[tot]=head[x];head[x]=tot;
    tot++;bian[tot]=x;zhi[tot]=z;nxt[tot]=head[y];head[y]=tot;
}
void build(int m){
    for(int i=1;i<=m;i++){
        int x,y;
        long long z;
        scanf("%d%d%lld",&x,&y,&z);
        add(x,y,z);
    }
}
void rebuild(){
    for(int i=1;i<=tot;i+=2){
        zhi[i]=zhi[i^1]=(zhi[i]+zhi[i^1])/2;
    }
}
int v[N];
void cut(int x){
    v[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        if(zhi[i]&&!v[bian[i]])cut(bian[i]);
    }
}
int d[N];
queue<int>q;
bool bfs(int b,int e){
    memset(d,0,sizeof(d));
    while(!q.empty())q.pop();
    q.push(b);d[b]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nxt[i]){
            if(zhi[i] && !d[bian[i]]){
                q.push(bian[i]);
                d[bian[i]]=d[x]+1;
                if(bian[i]==e)return 1;
            }
        }
    }
    return 0;
}
long long dinic(int b,int e,int x,long long flow){
    if(x==e)return flow;
    long long rest=flow,k;
    for(int i=head[x];i && rest;i=nxt[i]){
        if(zhi[i] && d[bian[i]]==d[x]+1){
            k=dinic(b,e,bian[i],min(rest,zhi[i]));
            if(!k)d[bian[i]]=0;
            zhi[i]-=k;
            zhi[i^1]+=k;
            rest-=k;
        }
    }
    return flow-rest;
}
inline long long maxflow(int b,int e){
    long long flow=0,maxflow=0;
    while(bfs(b,e)){
        while(flow=dinic(b,e,b,inf))maxflow+=flow;
    }
    return maxflow;
}
int b,e;
void solve(int l,int r){
    if(l==r)return;
    rebuild();
    b=a[l],e=a[r];
    re long long mincut=maxflow(b,e);
    memset(v,0,sizeof v);
    cut(b);
    for(re int i=1;i<=n;++i){
        if(!v[i])continue;
        for(re int j=1;j<=n;++j){
            if(v[j])continue;
            ans[i][j]=ans[j][i]=min(ans[i][j],mincut);
        }
    }
    re int cnt=l-1;
    static int ls[N];
    for(re int i=l;i<=r;++i){
        if(v[a[i]]){
            ls[++cnt]=a[i];
        }
    }
    re int fj=cnt;
    for(re int i=l;i<=r;++i){
        if(!v[a[i]]){
            ls[++cnt]=a[i];
        }
    }
    for(re int i=l;i<=r;++i)a[i]=ls[i];
    solve(l,fj);
    solve(fj+1,r);
}
int main()
{
    int b,e,Q,T;
    cin>>T;
    while(T--){
        memset(ans,0x3f3f3f,sizeof ans);
        scanf("%d%d",&n,&m);
        init();
        build(m);
        for(int i=1;i<=n;++i){
            a[i]=i;
        }
        solve(1,n);
        scanf("%d",&Q);
        static long long as[40010];
        int js=0;
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                as[++js]=ans[i][j];
            }
        }
        sort(as+1,as+js+1);
        long long ask;
        for(int i=1;i<=Q;++i){
            scanf("%lld",&ask);
            int mid=lower_bound(as,as+js+1,ask+1)-as;
            while(mid>js||(as[mid]>ask&&mid))mid--;
            printf("%d\n",mid);
        }puts("");
    }
}

猜你喜欢

转载自www.cnblogs.com/zhenglier/p/10115952.html