首页 > 代码库 > BZOJ2561 最小生成树

BZOJ2561 最小生成树

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


思路:对于某一条边,如果用小于这条边边权的所有边中的一部分就能够使得这条边的两个端点连通,那么这一条边必然不会出现在最小生成树中。最大生成树同理。

那么等价于求出删除最少的边,使得小于该边权的边不能使得两个端点连通,大于该边权的边也不能使得两个端点连通。

于是分别建图求最小割即可。


Code:

#include <queue>
#include <cstdio>
#include <cstring>
#include <climits>
#include <iostream>
#include <algorithm>
using namespace std;
 
#define N 20010
#define M 200010
struct Edge {
    int f, t, len;
    void read() {
        scanf("%d%d%d", &f, &t, &len);
    }
}E[M];
 
#define INF 0x3f3f3f3f
queue<int> q;
struct Solver {
    int head[N], next[M << 1], end[M << 1], flow[M << 1], ind;
    int d[N], gap[N], stack[N], top, cur[N];
    void reset() {
        ind = top = 0;
        memset(head, -1, sizeof head);
    }
    void addedge(int a, int b, int _flow) {
        int q = ind++;
        end[q] = b;
        next[q] = head[a];
        head[a] = q;
        flow[q] = _flow;
    }
    void addedge_db(int a, int b, int _flow) {
        addedge(a, b, _flow);
        addedge(b, a, _flow);
    }
    void make(int a, int b, int _flow) {
        addedge(a, b, _flow);
        addedge(b, a, 0);
    }
    void bfs(int T) {
        memset(d, -1, sizeof d);
        memset(gap, 0, sizeof gap);
        ++gap[d[T] = 0];
        int i, j;
        q.push(T);
        while(!q.empty()) {
            i = q.front();
            q.pop();
            for(j = head[i]; j != -1; j = next[j])
                if (d[end[j]] == -1)
                    ++gap[d[end[j]] = d[i] + 1], q.push(end[j]);
        }
    }
    int Maxflow(int S, int T) {
        int Min, ins, res = 0;
        memcpy(cur, head, sizeof(cur));
        bfs(T);
        int u = S;
        while(d[S] < (T - S + 1)) {
            if (u == T) {
                Min = INF;
                for(int i = 0; i < top; ++i)
                    if (flow[stack[i]] < Min)
                        Min = flow[stack[i]], ins = i;
                for(int i = 0; i < top; ++i) {
                    flow[stack[i]] -= Min;
                    flow[stack[i] ^ 1] += Min;
                }
                res += Min;
                u = end[stack[top = ins] ^ 1];
            }
            if (u != T && !gap[d[u] - 1])
                break;
            bool find = 0;
            for(int &j = cur[u]; j != -1; j = next[j])
                if (flow[j] && d[end[j]] + 1 == d[u]) {
                    find = 1;
                    ins = j;
                    break;
                }
            if (find) {
                stack[top++] = ins;
                cur[u] = ins;
                u = end[ins];
            }
            else {
                Min = T - S + 1;
                for(int j = head[u]; j != -1; j = next[j]) {
                    if (flow[j] && d[end[j]] < Min) {
                        Min = d[end[j]];
                        cur[u] = j;
                    }
                }
                if (!--gap[d[u]])
                    break;
                ++gap[d[u] = Min + 1];
                if (u != S)
                    u = end[stack[--top] ^ 1];
            }
        }
        return res;
    }
}G;
 
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
     
    register int i, j;
    for(i = 1; i <= m; ++i)
        E[i].read();
     
    int u, v, L;
    scanf("%d%d%d", &u, &v, &L);
     
    int tot = 0;
    G.reset();
    G.make(0, u, INF);
    G.make(v, n + 1, INF);
    for(i = 1; i <= m; ++i)
        if (E[i].len < L)
            G.addedge_db(E[i].f, E[i].t, 1);
    tot += G.Maxflow(0, n + 1);
     
    G.reset();
    G.make(0, u, INF);
    G.make(v, n + 1, INF);
    for(i = 1; i <= m; ++i)
        if (E[i].len > L)
            G.addedge_db(E[i].f, E[i].t, 1);
    tot += G.Maxflow(0, n + 1);
     
    printf("%d", tot);
     
    return 0;
}


BZOJ2561 最小生成树