最小割树
最小割树就是通过分治建出一棵树,树上两点的最小割就等于原图上的最小割,树上两点路径唯一,其最小割就等于路径上边权的最小值。
建树时,任意选择两点最为 \(s,t\) 跑最小割,求得 \(ans_{s,t}\),并将其分为两个集合 \(S,T\),对于 \(x\in S,y\in T\),有 \(ans_{x,y}=ans_{y,x}=\min(ans_{s,t},ans_{x,s},ans_{t,y})\)。
之后对于这两部分再分别跑最小割,重复上述步骤,就能在理论(网络流的复杂度跑不满) \(O(n^3m)\) 的复杂度内建出一棵最小割树。
答案处理
建树
建树就是对于每一层分治将 \(s,t\) 连上边权为 \(ans_{s,t}\) 的边即可,\(ans_{s,t}\) 为本次最小割跑出的答案。
因为原图两点的最小割就等于树上两点路径上边权的最小值,根据这点就可以有很多种处理方式了:
- \(O(n)\) 预处理,之后 \(ans_{x,y}=\min(ans_{x,lca(x,y)},ans_{lca(x,y),y})\),总体查询复杂度 \(O(Q\log n)\)
- 树上倍增,查询复杂度也是 \(O(Q\log n)\)。
- 因为 \(n\) 是很小的,所以直接以每个点为根遍历一遍树预处理出答案也是可以的,复杂度 \(O(n^2)\)。
- ……
不建树
根据上面的式子,对于 \(x\in S,y\in T\),有 \(ans_{x,y}=ans_{y,x}=\min(ans_{s,t},ans_{x,s},ans_{t,y})\)。
根据这个式子,在分治的同时处理出答案即可,不需要把树建出来。
一些细节
- 每一次跑最小割前要将所有边恢复初始状态。
- 本题为无向图,要所以可以不用对于每一个边再建一条流量为 \(0\) 的反边了,建双向边即可。
- 注意分治时每层的 \(s,t\) 不同,根据需要保存本层的 \(s,t\)。
- 因为数据有误,给定图的真实点数应该是 \(n+1\) 个,编号为 \(0\) 到 \(n\)。
代码如下
使用的时不把树建出来的方法。
#include<bits/stdc++.h>
#define ll long long
#define mkp make_pair
using namespace std;
const int N=510,M=3010,inf=0x3f3f3f3f;
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline bool blank(const char x) {return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp> inline void read(Tp &x) {x=0; register bool z=true; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=(x<<1)+(x<<3)+(a^48); x=(z?x:~x+1);}
inline void read(double &x) {x=0.0; register bool z=true; register double y=0.1; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=x*10+(a^48); if(a!='.') return x=z?x:-x,void(); for(a=gc();isdigit(a);a=gc(),y/=10) x+=y*(a^48); x=(z?x:-x);}
inline void read(char &x) {for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x) {register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) *x++=a; *x=0;}
inline void read(string &x) {x=""; register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) x+=a;}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y) {read(x),read(y...);}
template<typename Tp> inline void write(Tp x) {if(!x) return pc(48),void(); if(x<0) pc('-'),x=~x+1; register int len=0; register char tmp[64]; for(;x;x/=10) tmp[++len]=x%10+48; while(len) pc(tmp[len--]);}
inline void write(const double x) {register int a=6; register double b=x,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); pc('.'); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x) {register int a=x.first; if(a<7) {register double b=x.second,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); a&&(pc('.')); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);} else cout<<fixed<<setprecision(a)<<x.second;}
inline void write(const char x) {pc(x);}
inline void write(const bool x) {pc(x?49:48);}
inline void write(char *x) {fputs(x,stdout);}
inline void write(const char *x) {fputs(x,stdout);}
inline void write(const string &x) {fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y) {write(x),write(y...);}
int n,m,s,t,cs[N],ct[N],dep[N],node[N],ans[N][N];
int tot=1,now[N],head[N],nxt[M],to[M],w[M];
inline void add(int x,int y,int z)
{nxt[++tot]=head[x],to[head[x]=tot]=y,w[tot]=z;nxt[++tot]=head[y],to[head[y]=tot]=x,w[tot]=z;
}
inline bool bfs()
{queue<int>q; memset(dep,0,sizeof(dep));for(memcpy(now,head,sizeof(now)),q.push(s),dep[s]=1;!q.empty();){int x=q.front(); q.pop();for(int i=head[x],y;y=to[i];i=nxt[i]) if(w[i]&&!dep[y]){dep[y]=dep[x]+1,q.push(y); if(y==t) return true;}} return false;
}
inline int dfs(int x,int sum)
{if(x==t||!sum) return sum; int sur,res=0;for(int &i=now[x],y;y=to[i];i=nxt[i]) if(w[i]&&dep[y]==dep[x]+1){if(!(sur=dfs(y,min(sum,w[i])))) {dep[y]=-1; continue;}w[i]-=sur,w[i^1]+=sur,res+=sur; if(!(sum-=sur)) break;} return res;
}
inline void build(int l,int r)
{if(l>=r) return ; s=node[l],t=node[r]; int res=0,ns=0,nt=0;for(int i=2;i<tot;i+=2) w[i]=w[i^1]=w[i]+w[i^1]>>1;while(bfs()) res+=dfs(s,inf); ans[s][t]=ans[t][s]=res;for(int i=l,x;i<=r;i++) dep[x=node[i]]?cs[++ns]=x:ct[++nt]=x;for(int i=1;i<=ns;i++) node[l+i-1]=cs[i];for(int i=1;i<=nt;i++) node[l+ns+i-1]=ct[i];int os=s,ot=t; build(l,l+ns-1),build(l+ns,r); for(int i=1,j,x,y;i<=ns;i++) for(x=node[l+i-1],j=1;j<=nt;j++)y=node[l+ns+j-1],ans[x][y]=ans[y][x]=min({ans[x][os],ans[ot][y],res});
}
signed main()
{read(n,m),n++,memset(ans,0x3f,sizeof(ans));for(int x,y,z;m--;) read(x,y,z),add(x+1,y+1,z);for(int i=1;i<=n;i++) node[i]=i; build(1,n),read(m);for(int x,y;m--;) read(x,y),write(ans[x+1][y+1],'\n');
}