【bzoj2229】【ZJOI2229】【最小割】【最小割】

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<iostream>
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 200
#define M 3010
#define inf 2100000000
using 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(""); 
 }	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值