bzoj 2229: [Zjoi2011]最小割

it2022-07-05  155

Description

小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割” 现给定一张无向图,小白有若干个形如“图中有多少对点它们的最小割的容量不超过x呢”的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。

解题报告

对于最小割,其实本质不同的只有n-1个,考虑怎么找出这n-1个 可以简单证明:最小割不会相交,可以简单的用反证法证明 考虑一个四格矩形1,2,3,4四个区域(象限),分割线分别为A,B,C,D。 假设1,4的最小割为(A,C) 2,3的最小割为(B,D),那么可以推出 \(A<B\)\(C<D\),所以2,3的最小割不为 (A,C) 产生矛盾,固不相交.

既然不相交,考虑每次找出本质不同的,因为不相交,所以可以考虑分治. 找出当前层的最小割,化为S,T两个集合,然后再选择S,T集合分别做同样的操作,因为最小割不相交,所以找出的一定不同,对于一个点对,在每一个最小割里取Min即可

#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #define RG register #define il inline #define iter iterator #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int N=155,inf=2e9,M=3005; int id[N],n,m,b[N],head[N],nxt[M<<1],to[M<<1],dis[M<<1],num=1,a[N][N],T,S; void link(int x,int y,int z){nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;} void addedge(int x,int y,int z){link(x,y,z);link(y,x,z);} struct edge{int x,y,dis;}e[M]; int q[N],dep[N]; il bool bfs(){ int x,u,t=0,sum=1; memset(dep,0,sizeof(dep)); dep[S]=1;q[1]=S; while(t!=sum){ x=q[++t]; for(int i=head[x];i;i=nxt[i]){ u=to[i];if(dep[u] || dis[i]<=0)continue; dep[u]=dep[x]+1;q[++sum]=u; } } return dep[T]; } il int dfs(int x,int flow){ if(x==T || !flow)return flow; RG int u,tot=0,tmp; for(int i=head[x];i;i=nxt[i]){ u=to[i]; if(dis[i]<=0 || dep[u]!=dep[x]+1)continue; tmp=dfs(u,Min(flow,dis[i])); dis[i]-=tmp;dis[i^1]+=tmp; tot+=tmp;flow-=tmp; if(!flow)break; } if(!tot)dep[x]=-1; return tot; } il int maxflow(int ss,int tt){ S=ss;T=tt;int tot=0,tmp; while(bfs()){ tmp=dfs(S,inf); while(tmp)tot+=tmp,tmp=dfs(S,inf); } return tot; } void Clear(){memset(head,0,sizeof(head));num=1;} il void solve(int l,int r){ if(!(l^r))return ; int L=l,R=r; Clear(); for(RG int i=1;i<=m;i++) addedge(e[i].x,e[i].y,e[i].dis); int tmp=maxflow(id[l],id[r]); for(int i=1;i<=n;i++) if(dep[i])for(RG int j=1;j<=n;j++)if(!dep[j])a[i][j]=a[j][i]=Min(a[i][j],tmp); for(int i=l;i<=r;i++){ if(dep[id[i]])b[L++]=id[i]; else b[R--]=id[i]; } for(int i=l;i<=r;i++)id[i]=b[i]; solve(l,R);solve(L,r); } void work() { Clear(); int x,y,z; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); e[i].x=x;e[i].y=y;e[i].dis=z; } for(int i=1;i<=n;i++) for(RG int j=i+1;j<=n;j++) a[i][j]=a[j][i]=inf; for(int i=1;i<=n;i++)id[i]=i; solve(1,n); int Q;cin>>Q; while(Q--){ scanf("%d",&x); int ans=0; for(int i=1;i<=n;i++) for(RG int j=i+1;j<=n;j++) if(a[i][j]<=x)ans++; printf("%d\n",ans); } } int main() { int T;cin>>T; while(T--){ work(); if(T)puts(""); } return 0; }

转载于:https://www.cnblogs.com/Yuzao/p/7649605.html

相关资源:数据结构—成绩单生成器

最新回复(0)