【bzoj2561】【最小生成树】【最小割】

本文探讨了如何在给定的无向图中,通过删除最少数量的边,使得加入的一条特定边既有可能成为最小生成树的一部分,也有可能成为最大生成树的一部分。通过构建网络并应用最大流算法,实现最小割的概念来解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

 给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?

 

Input


  第一行包含用空格隔开的两个整数,分别为N和M;
  接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
  最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
  数据保证图中没有自环。
 

Output

 输出一行一个整数表示最少需要删掉的边的数量。

Sample Input

3 2
3 2 1
1 2 3
1 2 2

Sample Output

1

HINT

对于20%的数据满足N ≤ 10,M ≤ 20,L ≤ 20;

  对于50%的数据满足N ≤ 300,M ≤ 3000,L ≤ 200;

  对于100%的数据满足N ≤ 20000,M ≤ 200000,L ≤ 20000。

题解:考虑一条边存在于最小生成树里,

           如果删去这条边,剩下的比它权值小的边中一定不能使这条边的两端点联通。

           所以我们把比它权值小的边都建成一个网络。

           从这条边的一个端点向另一个端点跑最大流即可。

           最小割即是答案。

           最大生成树同理。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 20010
#define M 2000010
#define INF 2100000000
using namespace std;
int point[N],x,y,next[M*2],cnt(1),n,m,ans1,ans2,l;
int cur[N],pre[N],dis[N],gap[N],T,t;
bool f;
struct use{int st,en,v;}e[M*2];
struct use2{int st,en,v;}b[M*2];
bool cmp1(use2 a,use2 b){return a.v<b.v;}
bool cmp2(use2 a,use2 b){return a.v>b.v;}
void add(int x,int y,int v){
	//cout<<x<<' '<<y<<endl;
  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 en){
 int i,mn,u(ss),ans(0); 
 memset(gap,0,sizeof(gap));
 memset(dis,0,sizeof(dis));
 gap[0]=T;
 for (i=1;i<=T;i++) cur[i]=point[i];
 while(dis[ss]<T){
   f=false;
   for (i=cur[u];i;i=next[i])
    if (dis[e[i].en]+1==dis[u]&&e[i].v){cur[u]=i;f=true;break;}
   if (f){
    pre[u=e[i].en]=i;
    if (u==en){
      mn=INF;
      for (i=en;i!=ss;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
      ans+=mn;
      for (i=en;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=T,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;
}
int main(){
 scanf("%d%d",&n,&m);T=n;
 for (int i=1;i<=m;i++) scanf("%d%d%d",&b[i].st,&b[i].en,&b[i].v);
 scanf("%d%d%d",&x,&y,&l);sort(b+1,b+m+1,cmp1);t=1;
 while(b[t].v<l&&t<=m){add(b[t].st,b[t].en,1),t++;}
 ans1=isap(x,y);
 memset(point,0,sizeof(point));cnt=1;
 sort(b+1,b+m+1,cmp2);t=1;
 while(b[t].v>l&&t<=m){add(b[t].st,b[t].en,1);t++;}
 ans2=isap(x,y);
 cout<<ans1+ans2<<endl;
}


           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值