1. 程式人生 > >最小割(zjoi2011,bzoj2229)(最小割樹)

最小割(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("");
    }
}