【BZOJ】2561: 最小生成树【网络流】【最小割】

2561: 最小生成树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2685  Solved: 1253
[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。


Solution

完全看不出是网络流QAQ.....

很神奇的想法...

要使加的边在最小生成树上,就要使原图中所有能连接$u,v$的边并且长度小于$L$的通路被切断,最大生成树同理。

所以就是以$u,v$为源汇点跑最小割。重新建边。

为什么可以建双向边?因为这道题没确定方向,所以双向边是必要的。而在bfs中从源点出发更新其他点的$dep$,相当于是确定了方向,所以双向边也可以跑最小割了。

Code

#include<bits/stdc++.h>
using namespace std;

inline void read(int &x) {
    x = 0; char ch = getchar();
    while(ch > '9' || ch < '0')    ch = getchar();
    while(ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
}

struct Node {
    int u, v, f, nex;
    Node(int u = 0, int v = 0, int nex = 0, int f = 0) :
        u(u), v(v), nex(nex), f(f) { }
} Edge[4000005];

struct Init {
    int u, v, w;
} a[200005];

int stot = 1, h[20005];
void add(int u, int v, int f) {
    Edge[++stot] = Node(u, v, h[u], f);
    h[u] = stot;
    Edge[++stot] = Node(v, u, h[v], 0);
    h[v] = stot;
}

int n, m, s, t, w;
int vis[20005], dep[20005];
bool bfs() {
    memset(vis, 0, sizeof(vis));
    memset(dep, 0, sizeof(dep));
    queue < int > q;
    q.push(s); vis[s] = 1;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(!vis[v] && Edge[i].f) {
                dep[v] = dep[u] + 1;
                vis[v] = 1;
                q.push(v);
            }
        }
    }
    return vis[t];
}

int dfs(int u, int delta) {
    if(u == t || !delta)    return delta;
    int res = 0;
    for(int i = h[u]; i && delta; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(dep[v] == dep[u] + 1 && Edge[i].f) {
            int dd = dfs(v, min(delta, Edge[i].f));
            Edge[i].f -= dd;
            Edge[i ^ 1].f += dd;
            res += dd;    delta -= dd;
        }
    }
    return res;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i ++)
        read(a[i].u), read(a[i].v), read(a[i].w);
    read(s), read(t), read(w);
    for(int i = 1; i <= m; i ++)
        if(a[i].w < w)    add(a[i].u, a[i].v, 1),    add(a[i].v, a[i].u, 1);
    int t1 = 0, t2 = 0;
    while(bfs())    t1 += dfs(s, 0x3f3f3f3f);
    stot = 1; memset(h, 0, sizeof(h));
    for(int i = 1; i <= m; i ++)
        if(a[i].w > w)    add(a[i].u, a[i].v, 1),    add(a[i].v, a[i].u, 1);
    while(bfs())    t2 += dfs(s, 0x3f3f3f3f);
    printf("%d", t1 + t2);
    return 0;
}

 

posted @ 2018-10-17 21:57  Wans_ovo  阅读(242)  评论(0编辑  收藏  举报