定义:定义一棵树 $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; }