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

浙公网安备 33010602011771号