分治最小割 学习总结

这是一种可以较快的求解任意两点间最小割的算法

正常的暴力的话我们要枚举点对,一共要做O(n^2)次网络流

而我们注意到设某一个S->T最小割中两个点x,y,满足x在S集合且y在T集合中

设S->T的最小割为C,x->y的最小割为W

则一定有C>=W

若取得大于号,则x->y的最小割中一定有一个属于S集合点现在属于y集合或者一个属于T集合的点现在属于x集合

这样我们就可以分治处理并每次更新答案

实际上这样操作构成了一棵树,我们称之为最小割树,其任意两点的最小割等价于两点在树上路径的最小边权

注意每次更新的时候要更新S->T的所有点对,而不是分治的那部分

因为大小为n的集合分裂次数为n-1次,所以我们只需要做O(n)次网络流即可求解

具体算法是这样的:

1、我们在当前可选点集中任取S,T,做最小割

2、用当前最小割把所以满足i在S集合中且j在T集合中的点对(i,j)更新答案(取min)

3、分治处理S集合和T集合

 

CQOI 不同的最小割

OwO 直接分治+最小割,会得到O(n)个割,然后去重一下即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
 
const int maxn=1010;
const int oo=0x7fffffff;
int n,m,u,v,w,S,T,ans;
int h[maxn],cnt=1,tim;
int a[maxn],t1[maxn],t2[maxn];
int vis[maxn],check[maxn];
struct edge{
    int to,next,w;
}G[50010];
set<int>V;
queue<int>Q;
 
void add(int x,int y,int z){
    ++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt;
}
void read(int &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
bool BFS(){
    memset(vis,-1,sizeof(vis));Q.push(S);vis[S]=0;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=h[u];i;i=G[i].next){
            int v=G[i].to;
            if(vis[v]==-1&&G[i].w>0){
                vis[v]=vis[u]+1;
                Q.push(v);
            }
        }
    }return vis[T]!=-1;
}
int DFS(int x,int f){
    if(x==T||f==0)return f;
    int w,used=0;
    for(int i=h[x];i;i=G[i].next){
        if(vis[G[i].to]==vis[x]+1){
            w=f-used;
            w=DFS(G[i].to,min(w,G[i].w));
            G[i].w-=w;G[i^1].w+=w;
            used+=w;if(used==f)return used;
        }
    }
    if(!used)vis[x]=-1;
    return used;
}
void Get_Graph(){for(int i=2;i<=cnt;i+=2)G[i].w=G[i^1].w=(G[i].w+G[i^1].w)>>1;}
void dinic(){
    ans=0;Get_Graph();
    while(BFS())ans+=DFS(S,oo);
}
void DFS(int u){
    if(check[u]==tim)return;
    check[u]=tim;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(G[i].w>0)DFS(v);
    }return;
}
void Get_ans(int L,int R){
    if(L==R)return;
    S=a[L];T=a[R];
    dinic();V.insert(ans);
    ++tim;DFS(S);
    int l1=0,l2=0;
    for(int i=L;i<=R;++i){
        if(check[a[i]]==tim)t1[++l1]=a[i];
        else t2[++l2]=a[i];
    }
    for(int i=1;i<=l1;++i)a[L+i-1]=t1[i];
    for(int i=1;i<=l2;++i)a[L+l1+i-1]=t2[i];
    Get_ans(L,L+l1-1);Get_ans(L+l1,R);
}
 
int main(){
    read(n);read(m);
    for(int i=1;i<=n;++i)a[i]=i;
    for(int i=1;i<=m;++i){
        read(u);read(v);read(w);
        add(u,v,w);add(v,u,w);
    }
    Get_ans(1,n);
    ans=V.size();
    printf("%d\n",ans);
    return 0;
}

ZJOi 最小割

求出任意两点间最小割,然后就随意做了OwO

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
 
const int maxn=210;
const int oo=0x7fffffff;
int t,n,m,q,x,S,T,ans;
int u,v,w;
int h[maxn],cnt=1;
int fa[maxn];
struct edge{
    int to,next,w;
}G[100010];
int a[maxn],t1[maxn],t2[maxn];
int vis[maxn],check[maxn],tim;
int Ans[maxn][maxn];
queue<int>Q;
void read(int &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void add(int x,int y,int z){
    ++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt;
}
bool BFS(){
    memset(vis,-1,sizeof(vis));vis[S]=0;Q.push(S);
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=h[u];i;i=G[i].next){
            int v=G[i].to;
            if(vis[v]==-1&&G[i].w>0){
                vis[v]=vis[u]+1;
                Q.push(v);
            }
        }
    }return vis[T]!=-1;
}
int DFS(int x,int f){
    if(x==T||f==0)return f;
    int w,used=0;
    for(int i=h[x];i;i=G[i].next){
        if(vis[G[i].to]==vis[x]+1){
            w=f-used;
            w=DFS(G[i].to,min(w,G[i].w));
            G[i].w-=w;G[i^1].w+=w;
            used+=w;if(used==f)return used;
        }
    }
    if(!used)vis[x]=-1;
    return used;
}
void DFS(int u){
    if(check[u]==tim)return;
    check[u]=tim;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(G[i].w>0)DFS(v);
    }
}
void Get_Graph(){for(int i=2;i<=cnt;i+=2)G[i].w=G[i^1].w=(G[i].w+G[i^1].w)>>1;}
void dinic(){
    ans=0;Get_Graph();
    while(BFS())ans+=DFS(S,oo);
}
void Get_ans(int L,int R){
    if(L==R)return;
    S=a[L];T=a[R];
    dinic();
    tim++;DFS(S);
    int l1=0,l2=0;
    for(int i=L;i<=R;++i){
        if(check[a[i]]==tim)t1[++l1]=a[i];
        else t2[++l2]=a[i];
    }
    for(int i=1;i<=l1;++i)a[L+i-1]=t1[i];
    for(int i=1;i<=l2;++i)a[L+l1+i-1]=t2[i];
    for(int i=1;i<=n;++i){
        if(check[i]==tim){
            for(int j=1;j<=n;++j){
                if(check[j]!=tim){
                    int u=i,v=j;
                    if(u>v)swap(u,v);
                    Ans[u][v]=min(Ans[u][v],ans);
                }
            }
        }
    }
    Get_ans(L,L+l1-1);Get_ans(L+l1,R);
}
 
int main(){
    scanf("%d",&t);
    while(t--){
        read(n);read(m);
        memset(h,0,sizeof(h));cnt=1;
        for(int i=1;i<=m;++i){
            read(u);read(v);read(w);
            add(u,v,w);add(v,u,w);
        }
        for(int i=1;i<=n;++i)a[i]=i;
        memset(Ans,0x3f,sizeof(Ans));
        Get_ans(1,n);
        read(q);
        while(q--){
            read(x);ans=0;
            for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)if(Ans[i][j]<=x)ans++;
            printf("%d\n",ans);
        }printf("\n");
    }return 0;
}

  

posted @ 2016-07-07 07:14  _Vertical  阅读(331)  评论(0编辑  收藏  举报