分治最小割 学习总结
这是一种可以较快的求解任意两点间最小割的算法
正常的暴力的话我们要枚举点对,一共要做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; }