bzoj 2561: 最小生成树

Posted 蒟蒻JHY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2561: 最小生成树相关的知识,希望对你有一定的参考价值。

2561: 最小生成树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2248  Solved: 1073
[Submit][Status][Discuss]

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

 
 
 
 
首先我们要明确一个最小生成树的性质(貌似是切割性质还是啥的忘了),
那就是如果一条边在它所在的任意环里都是最小的话,那么它肯定是在最小生成树中的;反之亦然。
最大生成树的情况类似。
 
如果要让这条边在最小生成树中,那么它和比它小的边所构成的图中不能有环,也就是u和v除了这条直接连接的边没有路径联通。
这不就是u到v的最小割吗?
最大生成树的情况类似,而且非常好的一点是,
所有比(u,v)小的边和比(u,v)大的边是相互独立的,
这就提示我们可以单独求解,最后加起来就行了。
 
/**************************************************************
    Problem: 2561
    User: JYYHH
    Language: C++
    Result: Accepted
    Time:1332 ms
    Memory:18980 kb
****************************************************************/
 
#include<bits/stdc++.h>
#define ll long long
#define maxn 40005
#define pb push_back
using namespace std;
const int inf=1<<29;
vector<int> g[maxn];
struct lines{
    int to,flow,cap;
}l[maxn*20];
int t=-1,S,T,n;
int d[maxn],cur[maxn];
bool v[maxn];
 
inline void add(int xx,int yy,int zz){
    l[++t]=(lines){yy,0,zz},g[xx].pb(t);
    l[++t]=(lines){xx,0,0},g[yy].pb(t);
}
 
inline bool bfs(){
    queue<int> q;
    memset(v,0,sizeof(v));
    d[S]=0,v[S]=1,q.push(S);
     
    int x; lines e;
    while(!q.empty()){
        x=q.front(),q.pop();
        for(int i=g[x].size()-1;i>=0;i--){
            e=l[g[x][i]];
            if(!v[e.to]&&e.flow<e.cap){
                d[e.to]=d[x]+1;
                v[e.to]=1;
                q.push(e.to);
            }
        }
    }
 
    return v[T];
}
 
int dfs(int x,int a){
    if(x==T||!a) return a;
    int flow=0,f,sz=g[x].size();
    for(int &i=cur[x];i<sz;i++){
        lines &e=l[g[x][i]];
        if(d[x]==d[e.to]-1&&(f=dfs(e.to,min(a,e.cap-e.flow)))){
            flow+=f,a-=f;
            e.flow+=f,l[g[x][i]^1].flow-=f;
            if(!a) break;
        }
    }
     
    return flow;
}
 
inline int max_flow(){
    int an=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        an+=dfs(S,inf);
    }
    return an;
}
 
inline void init(){
    for(int i=1;i<=n;i++) g[i].clear();
    t=-1;
}
 
int m,W,ww[maxn*5];
int uu[maxn*5],vv[maxn*5];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",uu+i,vv+i,ww+i);
    }
    scanf("%d%d%d",&S,&T,&W);
     
    int ans=0;
    for(int i=1;i<=m;i++) if(ww[i]<W){
        add(uu[i],vv[i],1);
        add(vv[i],uu[i],1);
    }
    ans+=max_flow();
     
    init();
     
    for(int i=1;i<=m;i++) if(ww[i]>W){
        add(uu[i],vv[i],1);
        add(vv[i],uu[i],1);
    }
    ans+=max_flow();
     
    printf("%d\n",ans);
    return 0;
}

 

以上是关于bzoj 2561: 最小生成树的主要内容,如果未能解决你的问题,请参考以下文章

bzoj千题计划322:bzoj2561: 最小生成树(最小割)

BZOJ 2561 最小生成树

bzoj 2561: 最小生成树最小割

20160522~20160528

bzoj3732 -- 最小生成树+倍增

bzoj 1083: [SCOI2005]繁忙的都市 (最小生成树)