bzoj4519: [Cqoi2016]不同的最小割【最小割树】

题目大意:

求一张无向图中两两点之间不同的最小割的种数,n<=850,m<=8500;

解题思路:

即是要构造最小割树,统计不同边权的数量。
有一个结论:任意两点之间的最小割,不同的只有n-1个(然而不会证明……)
构造最小割树方法如下(分治+最小割):
1.集合中随便找两个点,求这两点的最小割
2.用求出的最小割更新s,t两个集合之间点的最小割(或者可以直接建边,那么最终两点之间的最小割就是两点之间路径的最小权值
3.对s,t两个集合递归处理

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=1005,M=20005,INF=1e9;
int n,m,ans,S,T,a[N],b[N],dis[N],tag[N];
int tot=1,cur[N],first[N],nxt[M],to[M],cap[M];
map<int,int>mp;queue<int>q;
void add(int x,int y,int z)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=z;
    nxt[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=z;
}
bool bfs()
{
    for(int i=1;i<=n;i++)dis[i]=-1,cur[i]=first[i];
    while(!q.empty())q.pop();
    dis[S]=0,q.push(S);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int e=first[u];e;e=nxt[e])
        {
            int v=to[e];
            if(dis[v]==-1&&cap[e])
            {
                dis[v]=dis[u]+1;
                if(v==T)return true;
                q.push(v);
            }
        }
    }
    return false;
}
int dfs(int u,int flow)
{
    if(u==T)return flow;
    int res=0;
    for(int &e=cur[u];e;e=nxt[e])
    {
        int v=to[e];
        if(dis[v]==dis[u]+1&&cap[e])
        {
            int det=dfs(v,min(cap[e],flow-res));
            cap[e]-=det,cap[e^1]+=det;
            res+=det;if(res==flow)break;
        }
    }
    if(res<flow)dis[u]=-1;
    return res;
}
void dfs(int u)
{
    tag[u]=1;
    for(int e=first[u];e;e=nxt[e])
        if(!tag[to[e]]&&cap[e])dfs(to[e]);
}
void solve(int l,int r)
{
    if(l==r)return;
    for(int i=2;i<=tot;i++)cap[i]=cap[i^1]=(cap[i]+cap[i^1])/2;
    S=a[l],T=a[r];int res=0;
    while(bfs())res+=dfs(S,INF);
    if(!mp[res])ans++,mp[res]=1;
    for(int i=1;i<=n;i++)tag[i]=0;
    dfs(S);
    int t1=l-1,t2=r+1;
    for(int i=l;i<=r;i++)
        tag[a[i]]?b[++t1]=a[i]:b[--t2]=a[i];
    for(int i=l;i<=r;i++)a[i]=b[i];
    solve(l,t1),solve(t2,r);
}
int main()
{
    //freopen("lx.in","r",stdin);
    int x,y;
    n=getint(),m=getint();
    while(m--)x=getint(),y=getint(),add(x,y,getint());
    for(int i=1;i<=n;i++)a[i]=i;
    solve(1,n);
    cout<<ans<<'\n';
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80251534