【bzoj2229】【ZJOI2229】【最小割】【最小割】
发布日期:2021-11-16 15:38:26 浏览次数:6 分类:技术文章

本文共 3184 字,大约阅读时间需要 10 分钟。

Description

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

Input

输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数n,m,表示图的点数和边数。 下面m行,每行3个正整数u,v,c(1<=u,v<=n,0<=c<=106),表示有一条权为c的无向边(u,v) 接下来一行,包含一个整数q,表示询问的个数 下面q行,每行一个整数x,其含义同题目描述。

Output

对于每组测试数据,输出应包括q行,第i行表示第i个问题的答案。对于点对(p,q)和(q,p),只统计一次(见样例)。

两组测试数据之间用空行隔开。

Sample Input

1
5 0
1
0

Sample Output

10

【数据范围】
对于100%的数据 T<=10,n<=150,m<=3000,q<=30,x在32位有符号整数类型范围内。
图中两个点之间可能有多条边
题解:首先有一个结论,本质不同的最小割只有n-1个,它们之间形成了一棵树。
知道了这个我们就可以每次分治求解出这n-1个最小割。
每次暴力更新两点之间的最小割即可。
代码:
#include
#include
#include
#include
#define N 200#define M 3010#define inf 2100000000using namespace std;int P,T,point[N],q,num,next[M<<2],b[N],c[N],n,m,x,y,z;int cur[N],dis[N],pre[N],gap[N],a[N],ans[N][N],cnt(1);bool f,vis[N];struct use{int st,en,v;}e[M<<3],aa[M<<3];int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}void add(int x,int y,int v){ next[++cnt]=point[x];point[x]=cnt;e[cnt].st=x;e[cnt].en=y;e[cnt].v=v; next[++cnt]=point[y];point[y]=cnt;e[cnt].st=y;e[cnt].en=x;e[cnt].v=v;}int isap(int ss,int tt){ int u(ss),mn,i,ans(0); memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); gap[0]=n; for (int i=1;i<=n;i++) cur[i]=point[i]; while (dis[ss]<=n){ f=false; for (i=cur[u];i;i=next[i]) if (e[i].v&&dis[e[i].en]+1==dis[u]){f=true;cur[u]=i;break;} if (f){ pre[u=e[i].en]=i; if (u==tt){ mn=inf; for (i=tt;i!=ss;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v); ans+=mn; for (i=tt;i!=ss;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn; u=ss; } } else{ gap[dis[u]]--;if (!gap[dis[u]]) return ans; for (mn=n,i=point[u];i;i=next[i]) if (e[i].v) mn=min(mn,dis[e[i].en]); gap[dis[u]=mn+1]++;cur[u]=point[u];if (u!=ss) u=e[pre[u]].st; } } return ans;}void dfs(int x){ vis[x]=1; for (int i=point[x];i;i=next[i]) if (!vis[e[i].en]&&e[i].v) dfs(e[i].en);}void solve(int l,int r){ if (l==r) return; for (int i=2;i<=cnt;i++) e[i]=aa[i]; int t=isap(a[l],a[r]),L(l),R(r),tt(0); memset(vis,0,sizeof(vis)); dfs(a[l]); for (int i=1;i<=n;i++) if (vis[i]) for (int j=1;j<=n;j++) if (!vis[j]) ans[i][j]=ans[j][i]=min(ans[i][j],t); for(int i=l;i<=r;i++) if(vis[a[i]]) b[L++]=a[i]; else b[R--]=a[i]; for(int i=l;i<=r;i++)a[i]=b[i]; solve(l,L-1);solve(R+1,r); }int main(){ //freopen("mincuto1.in","r",stdin); scanf("%d",&P); while (P--){ n=read();m=read();T=n;cnt=1; memset(ans,127/3,sizeof(ans)); memset(point,0,sizeof(point)); for (int i=1;i<=m;i++){ x=read();y=read();z=read(); add(x,y,z); } for (int i=1;i<=n;i++) a[i]=i; for (int i=2;i<=cnt;i++) aa[i]=e[i]; solve(1,n); q=read(); for (int i=1;i<=q;i++){ x=read();num=0; for (int j=1;j<=n;j++) for (int k=j+1;k<=n;k++) if (ans[j][k]<=x) num++; printf("%d\n",num); } puts(""); } }

转载地址:https://blog.csdn.net/sunshinezff/article/details/51013436 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【bzoj3144】【HNOI2013】【切糕】【最小割】
下一篇:【bzoj1565】【NOI2009】【植物大战僵尸】【拓扑排序+最小割】

发表评论

最新留言

很好
[***.229.124.182]2024年04月25日 21时58分41秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章