【BZOJ2229】最小割【BZOJ4519】不同的最小割

同一个板子懒得写两篇了。。。

主要思路就是说n个点的无向图中最多有n-1个不同的最小割,更详细一点说,当我们对跑一次网络流,得到(S,T)两个点集,只需要分别在两个点集内部选点跑最大流即可。

也就是说如果我们从S,T中再任意各选一个点跑最大流,这个最大流一定会出现在我们用分治跑的最大流之中。

这样,我们求一个图中任意两个点的最小割的时间复杂度就从n^2次最大流变成了n次最大流。

还有一个坑点,2229不知道,4519如果是把一条无向边拆成两条有向边会被卡常数,有特殊的建图技巧。。。

UPD:ZJOJ今年DAY2 的讲课某神犇讲的就是这个东西,看完讲稿受益匪浅。。。知道了这东西居然有非递归写法还能把树建出来。。。但我不知道PPT讲稿方不方便放。。。(UOJ群里有大家自己去找吧233)

/************************************************************** 
    Problem: 4519 
    User: RicardoWang 
    Language: C++ 
    Result: Accepted 
    Time:3764 ms 
    Memory:1692 kb 
****************************************************************/
  
#include<cstdlib> 
#include<cstdio> 
#include<iostream> 
#include<cstring> 
#include<cmath> 
#include<algorithm> 
#include<queue> 
#include<vector> 
using namespace std; 
#define maxn 1005 
#define maxm 10005 
#define oo 999999999 
struct edge 
{ 
    int u,v,c,f,next; 
}e[maxm*2]; 
int edge_ct,head[maxn],n,m; 
void add(int x,int y,int z) 
{ 
    e[++edge_ct]=(edge){x,y,z,0,head[x]}; head[x]=edge_ct; 
} 
void _read(int &x) 
{ 
    bool flag=false; char ch=getchar(); x=0; 
    while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();} 
    while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();} return; 
} 
void Init() 
{ 
    _read(n); _read(m); 
    int x,y,z; 
    edge_ct=0; 
    for(int i=1;i<=m;i++) 
    { 
        _read(x); _read(y); _read(z); 
        add(x,y,z); add(y,x,z); 
    } 
    return ; 
} 
int s,t,d[maxn],cur[maxn]; 
bool BFS() 
{ 
    for(int i=1;i<=n;i++)d[i]=0; 
    d[s]=1; queue<int>q; q.push(s); 
    int i,id; 
    while(!q.empty()) 
    { 
        i=q.front(); q.pop(); 
        for(id=head[i];id;id=e[id].next) 
        { 
            if(e[id].c<=e[id].f || d[e[id].v])continue; 
            d[e[id].v]=d[i]+1; 
            q.push(e[id].v);  
        } 
    } 
    return (d[t]>0); 
} 
int DFS(int now,int a) 
{ 
    if(now==t || a==0)return a; 
    int ans=0,f,j; 
    for(int &id=cur[now];id;id=e[id].next) 
    { 
        j=e[id].v; 
        if(d[j]==d[now]+1 && (f=DFS(j,min(a,e[id].c-e[id].f)))>0) 
        { 
            ans+=f; 
            e[id].f+=f; 
            e[id+ (id%2==0 ? -1 : 1)].f-=f; 
            a-=f; 
            if(!a)break; 
        } 
    } 
    return ans; 
} 
int Dinic(int x,int y) 
{ 
    s=x; t=y; 
    int ans=0; 
    while(BFS()) 
    { 
        for(int i=1;i<=n;i++)cur[i]=head[i]; 
        ans=ans+DFS(s,oo); 
    } 
    return ans; 
} 
int a[maxn],tmp[maxn]; 
bool v[maxn]; 
int ans[maxn],ans_cnt; 
void dfs_2(int now) 
{ 
    v[now]=true;  
    for(int id=head[now];id;id=e[id].next) 
    { 
        if(v[e[id].v] || e[id].c<=e[id].f)continue; 
        dfs_2(e[id].v); 
    } 
    return ; 
} 
void work(int l,int r) 
{ 
    if(l>=r)return ; 
    int t; 
    for(int i=1;i<=edge_ct;i++)e[i].f=0; 
    t=Dinic(a[l],a[r]); 
    ans[++ans_cnt]=t; 
    for(int i=1;i<=n;i++)v[i]=false; 
    dfs_2(a[l]); 
    int L=l,R=r; 
      
    for(int i=l;i<=r;i++) 
    { 
        if(v[a[i]]){tmp[L]=a[i]; L++;} 
        else {tmp[R]=a[i]; R--;} 
    } 
    for(int i=l;i<=r;i++) 
    { 
        a[i]=tmp[i]; 
    } 
    work(l,L-1); work(R+1,r); 
    return ; 
} 
int main() 
{ 
    //freopen("in.txt","r",stdin); 
    Init(); 
    for(int i=1;i<=n;i++)a[i]=i; 
    work(1,n); 
    sort(ans+1,ans+1+ans_cnt); 
    ans_cnt=unique(ans+1,ans+1+ans_cnt)-ans-1; 
    printf("%d\n",ans_cnt); 
    return 0; 
} 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值