zoukankan      html  css  js  c++  java
  • bzoj 2561: 最小生成树

    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。

    Source

    2012国家集训队Round 1 day1

    在众多大佬不小心说漏嘴后,本蒟蒻认识到这是一个神奇的最小割问题;

    以最小生成树为例,想一想LCT维护MST的操作,是如果加入边形成环,那么把环上的最大边弹去;

    那么我们如果想要使加入(u,v,l)使得其在MST内,那我们要使得u-->v不存在一条路径使得路径上所有的边权全部都小于L;

    (因为只要路径上有一个边权大于等于l,加入的边就可以把这个大于l的边弹掉,从而进入MST,所以含大于l边权的路径不需要删边);

    所以我们只需要保留原图中小于l的路径(如果在此图中u,v连通则表示u-->v有一条路径使得路径上的所有边权都小于l,这样这条边就谁都弹不掉了)

    那么我们上面的目标就变为割掉最少的边使u,v不连通了,直接连图上最小割;

    最大生成树类似,所以两个最小割相加即可;(然而数组开小(无向边),调了1h+)

    // MADE BY QT666
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #define RG register
    using namespace std;
    typedef long long ll;
    const int N=500050;
    const int Inf=19260817;
    int gi(){
      int x=0,flag=1;
      char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
      return x*flag;
    }
    struct data{
      int x,y,w;
    }edge[N];
    vector<int>p[N];
    int head[N],to[N],nxt[N],s[N],n,m,S,T,L,q[N*10],level[N],F,cnt=1,hh;
    inline void Addedge(RG int x,RG int y,RG int z) {
      to[++cnt]=y,s[cnt]=z,nxt[cnt]=head[x],head[x]=cnt;
    }
    inline void lnk(RG int x,RG int y,RG int z){
      Addedge(x,y,z),Addedge(y,x,0);
    }
    inline bool bfs(){
      for(RG int i=1;i<=n;i++) level[i]=0;
      int t=0,sum=1;q[0]=S;level[S]=1;
      while(t<sum){
        int now=q[t++];
        if(now==T) return 1;
        for(int i=head[now];i;i=nxt[i]){
          int y=to[i];
          if(level[y]==0&&s[i]){
    	level[y]=level[now]+1;
    	q[sum++]=y;
          }
        }
      }
      return 0;
    }
    inline int dfs(RG int x,RG int maxf){
      if(x==T) return maxf;
      int ret=0;
      for(RG int i=head[x];i;i=nxt[i]){
        int y=to[i],f=s[i];
        if(level[y]==level[x]+1&&f){
          int minn=min(f,maxf-ret);
          f=dfs(y,minn);
          s[i]-=f,s[i^1]+=f;ret+=f;
          if(ret==maxf) break;
        }
      }
      if(!ret) level[x]=0;
      return ret;
    }
    inline void Dinic(){
      while(bfs()) F+=dfs(S,Inf);
    }
    int main(){
      n=gi(),m=gi();
      for(RG int i=1;i<=m;i++){
        edge[i].x=gi(),edge[i].y=gi(),edge[i].w=gi();
      }
      S=gi(),T=gi(),L=gi();
      for(RG int i=1;i<=m;i++){
        if(edge[i].w<L) lnk(edge[i].x,edge[i].y,1),lnk(edge[i].y,edge[i].x,1);
      }
      Dinic();
      memset(head,0,sizeof(head));cnt=1;
        for(RG int i=1;i<=m;i++){
        if(edge[i].w>L) lnk(edge[i].x,edge[i].y,1),lnk(edge[i].y,edge[i].x,1);
      }
      Dinic();
      printf("%d
    ",F);
      return 0;
    }
    
  • 相关阅读:
    了解 DICOM 基本协议与其相关
    C# PropertyInfo 反射实体部分字段
    ref(引用参数) 和 out(输出参数) 区别
    Linq Where Expression<Func<T,bool>> 查询条件
    随笔规范
    C# 集合分析
    C# 几种常用的数据类型
    关于 C# 方法参数的理解
    打算开始写博客了
    有趣的算法、逻辑面试题
  • 原文地址:https://www.cnblogs.com/qt666/p/6953709.html
Copyright © 2011-2022 走看看