最小割树学习笔记
定义:定义一棵树 $T$ 为最小割树,如果对于树上的所有边 $(u,v)$,树上去掉 $(u,v)$ 后产生的两个集合恰好是原图上 $(u,v)$ 的最小割把原图分成的两个集合,且边 $(u,v)$ 的权值等于原图上 $(u,v)$ 的最小割
最小割树性质:图中 $(s,t)$ 的最小割等于最小割树上 $s$ 到 $t$ 的路径上最小的边权
按照定义构造即可 $O(n^3m)$
#include<cstdio>
#define maxn 555
#define maxm 2222
#define INF 1000000000
inline int read(){
int r=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')r=(r<<1)+(r<<3)+(c^48),c=getchar();
return r*f;
}
inline int min(int a,int b){
return a<b?a:b;
}
inline void swap(int &a,int &b){
int c=a;
a=b;
b=c;
}
int n,m,q;
struct Flow{
struct E{
int v,c,nxt;
E() {}
E(int v,int c,int nxt):v(v),c(c),nxt(nxt) {}
}e[maxm*4];
int s,t,hd,tl,s_e,head[maxn],cur[maxn],lev[maxn],q[maxn];
inline void a_e(int u,int v,int c){
e[++s_e]=E(v,c,head[u]);
head[u]=s_e;
}
inline void add(int u,int v,int c){
a_e(u,v,c);
a_e(v,u,0);
}
inline void init(int S,int T){
s_e=1;
s=S,t=T;
for(int i=1;i<=n;i++)head[i]=0;
}
inline bool BFS(){
hd=tl=0;
for(int i=1;i<=n;i++)
lev[i]=0,cur[i]=head[i];
lev[s]=1;
q[++tl]=s;
while(hd^tl){
int u=q[++hd];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v,c=e[i].c;
if(!c||lev[v])continue;
lev[v]=lev[u]+1;
q[++tl]=v;
if(v==t)return true;
}
}
return false;
}
int dfs(int u,int f){
int d=0,used=0;
if(!(u^t))return f;
for(int &i=cur[u];i;i=e[i].nxt){
int v=e[i].v;
if(!e[i].c||(lev[v]^(lev[u]+1)))continue;
d=dfs(v,min(f-used,e[i].c));
if(!d)continue;
used+=d;
e[i].c-=d;
e[i^1].c+=d;
if(used==f)break;
}
if(!used)lev[u]=0;
return used;
}
inline int dinic(){
int max_flow=0,d=0;
while(BFS())
while((d=dfs(s,INF)))
max_flow+=d;
return max_flow;
}
}flow;
struct Tree{
struct E{
int v,w,nxt;
E() {}
E(int v,int w,int nxt):v(v),w(w),nxt(nxt) {}
}e[maxm*2];
int s_e,head[maxn],dot[maxn],be[maxn],uu[maxm],vv[maxm],cc[maxm];
bool vis[maxn];
inline void a_e(int u,int v,int w){
e[++s_e]=E(v,w,head[u]);
head[u]=s_e;
}
inline void init(){
s_e=0;
for(int i=1;i<=n;i++)
dot[i]=be[i]=i,head[i]=0;
for(int i=1;i<=m;i++)
uu[i]=read(),vv[i]=read(),cc[i]=read();
}
inline void add(){
for(int i=1;i<=m;i++){
flow.add(uu[i],vv[i],cc[i]);
flow.add(vv[i],uu[i],cc[i]);
}
}
inline int val(int s,int t){
flow.init(s,t),add();//每次都要重置
return flow.dinic();
}
inline void find(int l,int r){
for(int i=1;i<=flow.tl;i++){
int u=flow.q[i];
if(be[u]>=l&&be[u]<=r)vis[u]=1;
}
}
void build(int l,int r){
if(l>=r)return;
int ans=val(dot[l],dot[r]);
a_e(dot[l],dot[r],ans);
a_e(dot[r],dot[l],ans);
find(l,r);
int i=l,j=r,mid=l-1;
while(i<j){
while(vis[dot[i]])i++;//dinic里最后一次BFS因为没有增广路了,所以遍历到的就是割完了以后的集合
while(!vis[dot[j]])j--;
if(i>j)break;
swap(be[dot[i]],be[dot[j]]);
swap(dot[i],dot[j]);
i++,j--;
}
for(int i=l;i<=r;i++)
mid+=vis[dot[i]],vis[dot[i]]=0;
build(l,mid);
build(mid+1,r);
}
}tree;
struct LCA{
int er[22],lg[maxn],dep[maxn],anc[22][maxn],dp[22][maxn];
void dfs(int u,int fa){
anc[0][u]=fa;
dep[u]=dep[fa]+1;
for(int i=tree.head[u];i;i=tree.e[i].nxt){
int v=tree.e[i].v,w=tree.e[i].w;
if(v==fa)continue;
dp[0][v]=w;
dfs(v,u);
}
}
inline void init(){
dfs(1,1);
er[0]=1,lg[0]=-1,dp[0][1]=INF;
for(int i=1;i<=n;i++)lg[i]=lg[(i>>1)]+1;
for(int i=1;i<=lg[n];i++)er[i]=er[(i-1)]<<1;
for(int i=1;i<=lg[n];i++)
for(int j=1;j<=n;j++){
anc[i][j]=anc[i-1][anc[i-1][j]];
dp[i][j]=min(dp[i-1][j],dp[i-1][anc[i-1][j]]);
}
}
inline int ans(int u,int v){
int Min=INF;
if(dep[u]>dep[v])swap(u,v);
int c=dep[v]-dep[u];
while(c){
Min=min(Min,dp[lg[c]][v]);
v=anc[lg[c]][v];
c-=er[lg[c]];
}
if(u==v)return Min;
for(int i=lg[dep[u]];i>=0;i--){
if(anc[i][u]==anc[i][v])continue;
Min=min(Min,min(dp[i][u],dp[i][v]));
u=anc[i][u],v=anc[i][v];
}
return min(Min,min(dp[0][u],dp[0][v]));
}
}lca;
int main(){
n=read(),m=read();
tree.init();
tree.build(1,n);
q=read();
lca.init();
for(int i=1;i<=q;i++){
int x=read(),y=read();
printf("%d\n",lca.ans(x,y));
}
return 0;
}

浙公网安备 33010602011771号