【模板】最小割樹(Gomory-Hu Tree)-bzoj2229&4519
阿新 • • 發佈:2018-11-26
模板傳送門:洛谷【模板】最小割樹
相關的題目都很板。。。
題意
給定一個 個點 條邊的無向連通圖,多次詢問兩點之間的最小割。
題解
某定理:
個點的圖上,兩點之間只有
種本質不同的最小割。
所以存在一顆樹,滿足樹上兩點最小割等於原圖上兩點的最小割。
(不會證
最小割樹的具體構造方法:
每次在點集中任選兩個點分別作為源點和匯點跑最小割,在樹中將這兩個點連邊,邊權為最小割的值。將最小割劃分出的兩個互不相同的集合分別遞迴下去求解。
注意每次跑最小割都是在原圖的基礎上跑,所以要把每條邊流量復原。
構造出樹後,倍增回答即可。
還有另一種構造方法,這裡有詳細講解。
原理比較顯然,不再證明。
程式碼
#include<bits/stdc++.h> using namespace std; const int N=510,M=2e4+10,inf=0x3f3f3f3f; int n,m,fe,S,T; int head[N],cur[N],to[M],nxt[M],w[M],ori[M],tot=1; int ht[N],dt[N<<1],nt[N<<1],wt[N<<1],cnt; int dep[N],fa[N][15],dis[N][15],bin[20]; int rv[N],vs[N],rep[N],tim; char cp,OS[100]; inline void rd(int &x) { cp=getchar();x=0; for(;!isdigit(cp);cp=getchar()); for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48); } inline void ot(int x) { int re=0; for(;(!re)||(x);x/=10) OS[++re]='0'+x%10; for(;re;--re) putchar(OS[re]); putchar('\n'); } inline void lk(int u,int v,int vv) {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;ori[tot]=vv;} inline void lkk(int u,int v,int vv) {dt[++cnt]=v;nt[cnt]=ht[u];ht[u]=cnt;wt[cnt]=vv;} queue<int>que; inline bool bfs() { memset(dep,0xff,sizeof(dep)); dep[S]=1;que.push(S); int i,j,x; for(;que.size();){ x=que.front();que.pop(); for(i=head[x];i;i=nxt[i]){ j=to[i]; if((~dep[j])||(!w[i])) continue; dep[j]=dep[x]+1;que.push(j); } } return (dep[T]!=-1); } int dfs(int x,int f) { if(x==T) return f; int j,ss=0,res; for(int &i=cur[x];i;i=nxt[i]){ j=to[i];if(dep[j]!=dep[x]+1 || (!w[i])) continue; res=dfs(j,min(w[i],f-ss));if(!res) continue; w[i]-=res;w[i^1]+=res;ss+=res;if(ss==f) return ss; } if(!ss) dep[x]=-1; return ss; } void psh(int x) { vs[x]=tim; for(int i=head[x];i;i=nxt[i]) if(w[i]&&(vs[to[i]]!=tim)) psh(to[i]); } inline void rn(int l,int r) { if(l>=r) return; int res,i,ql=l,qr=r;fe=0;S=rv[l];T=rv[l+1]; memcpy(w,ori,sizeof(int)*(tot+1)); for(;bfs();){ memcpy(cur,head,sizeof(cur)); for(;;fe+=res) {res=dfs(S,inf);if(!res) break;}; } lkk(S,T,fe);lkk(T,S,fe);tim++;psh(S); for(i=l;i<=r;++i) vs[rv[i]]==tim?rep[ql++]=rv[i]:rep[qr--]=rv[i]; for(i=l;i<ql;++i) rv[i]=rep[i]; for(i=qr+1;i<=r;++i) rv[i]=rep[i]; rn(l,ql-1);rn(qr+1,r); } void df(int x) { int i,j,k; for(i=1;bin[i]<=dep[x];++i){ fa[x][i]=fa[fa[x][i-1]][i-1]; dis[x][i]=min(dis[x][i-1],dis[fa[x][i-1]][i-1]); } for(i=ht[x];i;i=nt[i]){ j=dt[i];if(j==fa[x][0]) continue; fa[j][0]=x;dis[j][0]=wt[i]; dep[j]=dep[x]+1;df(j); } } int main(){ int i,j,x,y,z,ans; memset(dis,0x3f,sizeof(dis)); bin[0]=1;for(i=1;i<=15;++i) bin[i]=bin[i-1]<<1; rd(n);rd(m); for(i=1;i<=m;++i){ rd(x);rd(y);rd(z); lk(x,y,z);lk(y,x,z); } for(i=1;i<=n;++i) rv[i]=i; rn(1,n);dep[1]=0;df(1); for(rd(m);m;--m){ rd(x);rd(y);ans=inf; if(dep[x]<dep[y]) swap(x,y); z=dep[x]-dep[y]; for(i=0;bin[i]<=z;++i) if(bin[i]&z){ ans=min(ans,dis[x][i]); x=fa[x][i]; } if(x!=y){ for(i=9;~i;--i) if(fa[x][i]!=fa[y][i]){ ans=min(ans,min(dis[x][i],dis[y][i])); x=fa[x][i];y=fa[y][i]; } ans=min(ans,min(dis[x][0],dis[y][0])); } ot(ans); } return 0; }
bzoj2229 zjoi2011最小割
構造出最小割樹後 判斷即可。
p.s.做這題的時候有個錯誤的思路:
並查集合並聯通塊,二分最大邊權時的答案。然而合併連通塊時兩邊的點之間的最小割不一定就是這條邊的權值。
#include<bits/stdc++.h>
using namespace std;
const int N=200,M=2e4+10,inf=0x3f3f3f3f;
int n,m,fe,S,T,tk;
int head[N],cur[N],to[M],nxt[M],w[M],ori[M],tot;
int dep[N],val[N][N];
int rv[N],vs[N],rep[N],tim;
char cp,OS[100];
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}
inline void ot(int x)
{
int re=0;
for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
for(;re;--re) putchar(OS[re]);
putchar('\n');
}
inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;ori[tot]=vv;}
queue<int>que;
inline bool bfs()
{
memset(dep,0xff,sizeof(dep));
dep[S]=1;que.push(S);
int i,j,x;
for(;que.size();){
x=que.front();que.pop();
for(i=head[x];i;i=nxt[i]){
j=to[i];
if((~dep[j])||(!w[i])) continue;
dep[j]=dep[x]+1;que.push(j);
}
}
return (dep[T]!=-1);
}
int dfs(int x,int f)
{
if(x==T) return f;
int j,ss=0,res;
for(int &i=cur[x];i;i=nxt[i]){
j=to[i];if(dep[j]!=dep[x]+1 || (!w[i])) continue;
res=dfs(j,min(w[i],f-ss));if(!res) continue;
w[i]-=res;w[i^1]+=res;ss+=res;if(ss==f) return ss;
}
if(!ss) dep[x]=-1;
return ss;
}
void psh(int x)
{
vs[x]=tim;
for(int i=head[x];i;i=nxt[i])
if(w[i]&&(vs[to[i]]!=tim)) psh(to[i]);
}
inline void rn(int l,int r)
{
if(l>=r) return;
int res,i,j,ql=l,qr=r;fe=0;S=rv[l];T=rv[l+1];
memcpy(w,ori,sizeof(int)*(tot+1));
for(;bfs();){
memcpy(cur,head,sizeof(cur));
for(;;fe+=res)
{res=dfs(S,inf);if(!res) break;};
}
tim++;psh(S);
for(i=1;i<=n;++i)
if(vs[i]==tim)
for(j=1;j<=n;++j)
if(vs[j]!=tim)
val[i][j]=val[j][i]=min(val[i][j],fe);
for(i=l;i<=r;++i)
vs[rv[i]]==tim?rep[ql++]=rv[i]:rep[qr--]=rv[i];
for(i=l;i<ql;++i) rv[i]=rep[i];
for(i=qr+1;i<=r;++i) rv[i]=rep[i];
rn(l,ql-1);rn(qr+1,r);
}
inline void sol()
{
int i,j,x,y,z,ans;
memset(val,0x3f,sizeof(val));
memset(head,0,sizeof(head));tot=1;
memset(vs,0,sizeof(vs));tim=0;
rd(n);rd(m);
for(i=1;i<=m;++i){
rd(x);rd(y);rd(z);
lk(x,y,z);lk(y,x,z);
}
for(i=1;i<=n;++i) rv[i]=i;rn(1,n);
for(rd(m);m;--m){
rd(x);ans=0;
for(i=1;i<n;++i)
for(j=i+1;j<=n;++j)
if(val[i][j]<=x) ans++;
ot(ans);
}
}
int main(){
for(rd(tk);tk;--tk) {sol();puts("");}
return 0;
}
bzoj4519 [Cqoi2016]不同的最小割
同上,直接求。
#include<bits/stdc++.h>
using namespace std;
const int N=900,M=2e4+10,inf=0x3f3f3f3f;
int n,m,fe,S,T,tk;
int head[N],cur[N],to[M],nxt[M],w[M],ori[M],tot=1;
int dep[N],val[N][N],ans[N*N],cot;
int rv[N],vs[N],rep[N],tim;
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}
inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;ori[tot]=vv;}
queue<int>que;
inline bool bfs()
{
memset(dep,0xff,sizeof(dep));
dep[S]=1;que.push(S);
int i,j,x;
for(;que.size();){
x=que.front();que.pop();
for(i=head[x];i;i=nxt[i]){
j=to[i];
if((~dep[j])||(!w[i])) continue;
dep[j]=dep[x]+1;que.push(j);
}
}
return (dep[T]!=-1);
}
int dfs(int x,int f)
{
if(x==T) return f;
int j,ss=0,res;
for(int &i=cur[x];i;i=nxt[i]){
j=to[i];if(dep[j]!=dep[x]+1 || (!w[i])) continue;
res=dfs(j,min(w[i],f-ss));if(!res) continue;
w[i]-=res;w[i^1]+=res;ss+=res;if(ss==f) return ss;
}
if(!ss) dep[x]=-1;
return ss;
}
void psh(int x)
{
vs[x]=tim;
for(int i=head[x];i;i=nxt[i])
if(w[i]&&(vs[to[i]]!=tim)) psh(to[i]);
}
inline void rn(int l,int r)
{
if(l>=r) return;
int res,i,j,ql=l,qr=r;fe=0;S=rv[l];T=rv[l+1];
memcpy(w,ori,sizeof(int)*(tot+1));
for(;bfs();){
memcpy(cur,head,sizeof(cur));
for(;;fe+=res)
{res=dfs(S,inf);if(!res) break;};
}
tim++;psh(S);
for(i=1;i<=n;++i)
if(vs[i]==tim)
for(j=1;j<=n;++j)
if(vs[j]!=tim)
val[i][j]=val[j][i]=min(val[i][j],fe);
for(i=l;i<=r;++i)
vs[rv[i]]==tim?rep[ql++]=rv[i]:rep[qr--]=rv[i];
for(i=l;i<ql;++i) rv[i]=rep[i];
for(i=qr+1;i<=r;++i) rv[i]=rep[i];
rn(l,ql-1);rn(qr+1,r);
}
int main(){
memset(val,0x3f,sizeof(val));
int i,j,x,y,z;
rd(n);rd(m);
for(i=1;i<=m;++i){
rd(x);rd(y);rd(z);
lk(x,y,z);lk(y,x,z);
}
for(i=1;i<=n;++i) rv[i]=i;rn(1,n);
for(i=1;i<n;++i)
for(j=i+1;j<=n;++j) ans[cot++]=val[i][j];
sort(ans,ans+cot);
cot=unique(ans,ans+cot)-ans;
printf("%d\n",cot);
return 0;
}