luoguP4897 【模板】最小割树(Gomory-Hu Tree)
吸氧才能过,写一个分治就行.
这里讲一下构造方法:考虑对点集 $U$ 求最小割树.
随便选取两个点 $u,v$ 然后跑一个 $u$ 到 $v$ 的最小割.
然后有两条性质:
- 对于 $U$ 中的每个点,一定被划分到了 $u$ 集合或 $v$ 集合(废话)
- 对于 $u$ 集合点到 $v$ 集合点的最小割一定小于等于 $u$ 到 $v$ 的最小割(最坏情况也可以取到 $u$ 到 $v$ 的最小割)
然后这么递归,一共跑 $n-1$ 次最大流,查询的时候查询树上路径最小边权就行了.
code:
#include <cstdio> #include <cstring> #include <string> #include <queue> #include <vector> #define N 503 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; namespace net { #define inf 1000000000 struct Edge { int u,v,c; Edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){} }; queue<int>q; vector<Edge>edges; vector<int>G[N]; int vv[N],vis[N],d[N],col[N],s,t; void add(int u,int v,int c) { edges.push_back(Edge(u,v,c)); edges.push_back(Edge(v,u,0)); int o=edges.size(); G[u].push_back(o-2); G[v].push_back(o-1); } int dfs(int x,int cur) { if(x==t) return cur; int an=0,flow=0; for(int i=vv[x];i<G[x].size();++i,++vv[x]) { Edge e=edges[G[x][i]]; if(e.c>0&&d[e.v]==d[x]+1) { an=dfs(e.v,min(cur,e.c)); if(an) { cur-=an; flow+=an; edges[G[x][i]].c-=an; edges[G[x][i]^1].c+=an; if(!cur) break; } } } return flow; } int bfs() { memset(vis,0,sizeof(vis)); d[s]=0; vis[s]=1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<G[u].size();++i) { if(edges[G[u][i]].c>0) { int v=edges[G[u][i]].v; if(!vis[v]) { vis[v]=1; d[v]=d[u]+1; q.push(v); } } } } return vis[t]; } int maxflow() { int re=0; while(bfs()) { memset(vv,0,sizeof(vv)); re+=dfs(s,inf); } return re; } void emp() { memset(vv,0,sizeof(vv)); memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); for(int i=0;i<edges.size();i+=2) { edges[i].c+=edges[i^1].c; edges[i^1].c=0; } } void mark(int x,int co) { if(col[x]==co) return; col[x]=co; for(int i=0;i<G[x].size();++i) { Edge e=edges[G[x][i]]; if(e.c>0) mark(e.v,co); } } }; int n,m,edges,id,dep[N]; int minn[15][N],fa[15][N]; int tmp1[N],tmp2[N],a[N],hd[N],to[N<<1],nex[N<<1],val[N<<1]; void add(int u,int v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } void solve(int l,int r) { if(l>=r) return; ++id; net::emp(); net::s=a[l]; net::t=a[l+1]; int cur=net::maxflow(); add(a[l],a[l+1],cur); add(a[l+1],a[l],cur); net::mark(a[l],id); int t1=0,t2=0; for(int i=l;i<=r;++i) if(net::col[a[i]]==id) tmp1[++t1]=a[i]; else tmp2[++t2]=a[i]; int L=l,R=r; for(int i=1;i<=t1;++i) a[L++]=tmp1[i]; for(int i=1;i<=t2;++i) a[L++]=tmp2[i]; solve(l,l+t1-1); solve(r-t2+1,r); } void dfs(int x,int ff) { dep[x]=dep[ff]+1; for(int i=1;i<15;++i) { fa[i][x]=fa[i-1][fa[i-1][x]]; minn[i][x]=min(minn[i-1][x],minn[i-1][fa[i-1][x]]); } for(int i=hd[x];i;i=nex[i]) { int y=to[i]; if(y==ff) continue; fa[0][y]=x; minn[0][y]=val[i]; dfs(y,x); } } int query(int x,int y) { if(dep[x]>dep[y]) swap(x,y); int re=inf; if(dep[x]!=dep[y]) { for(int i=14;i>=0;--i) if(dep[fa[i][y]]>=dep[x]) re=min(re,minn[i][y]),y=fa[i][y]; } if(x==y) return re; for(int i=14;i>=0;--i) { if(fa[i][x]!=fa[i][y]) { re=min(re,minn[i][x]); re=min(re,minn[i][y]); x=fa[i][x]; y=fa[i][y]; } } re=min(re,minn[0][x]); re=min(re,minn[0][y]); return re; } int main() { // setIO("input"); scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { int u,v,w; scanf("%d%d%d",&u,&v,&w); net::add(u,v,w); net::add(v,u,w); } for(int i=1;i<=n;++i) a[i]=i; memset(minn,0x3f,sizeof(minn)); solve(1,n),dfs(1,0); int Q; scanf("%d",&Q); for(int i=1;i<=Q;++i) { int x,y,ans; scanf("%d%d",&x,&y); ans=query(x,y); printf("%d\n",ans>=inf?-1:ans); } return 0; }