版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84394427
模板传送门:洛谷【模板】最小割树
相关的题目都很板。。。
题意
给定一个 个点 条边的无向连通图,多次询问两点之间的最小割。
题解
某定理:
个点的图上,两点之间只有
种本质不同的最小割。
所以存在一颗树,满足树上两点最小割等于原图上两点的最小割。
(不会证
最小割树的具体构造方法:
每次在点集中任选两个点分别作为源点和汇点跑最小割,在树中将这两个点连边,边权为最小割的值。将最小割划分出的两个互不相同的集合分别递归下去求解。
注意每次跑最小割都是在原图的基础上跑,所以要把每条边流量复原。
构造出树后,倍增回答即可。
还有另一种构造方法,这里有详细讲解。
原理比较显然,不再证明。
代码
#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;
}