1. 程式人生 > >BZOJ1977[BJOI2010] 次小生成樹

BZOJ1977[BJOI2010] 次小生成樹

con getchar() 過程 else if tin min cmp efi lse

題目藍鏈

Solution

考慮首先建出一棵最小生成樹,然後再枚舉所有其他的邊。然後直接查詢這條邊對應在樹上的兩點之間的鏈上最大值和次大值,因為要保證嚴格次小。然後用查詢的值更新一下答案

維護一條鏈上的最大值和次大值,直接倍增就行了

Code

#include <bits/stdc++.h>

using namespace std;

#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

inline int read() {
    int sum = 0, fg = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == ‘-‘) fg = -1;
    for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    return fg * sum;
}

const int maxn = 1e5 + 10;
const int maxm = 3e5 + 10;
const LL inf = 0x3f3f3f3f3f3f3f3f;

int n, m;
bool b[maxm];
struct edge {
    int x, y, z;
    bool operator < (const edge &t) const { return z < t.z; }
}e[maxm];

vector<pii> g[maxn];

namespace MST {

    int fa[maxn];
    int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }

    LL kruskal() {
        LL res = 0;
        sort(e + 1, e + m + 1);
        for (int i = 1; i <= n; i++) fa[i] = i;
        for (int i = 1; i <= m; i++) {
            int x = find(e[i].x), y = find(e[i].y);
            if (x == y) continue;
            g[e[i].x].push_back((pii){e[i].y, e[i].z});
            g[e[i].y].push_back((pii){e[i].x, e[i].z});
            fa[x] = y; res += e[i].z; b[i] = 1;
        }
        return res;
    }

}

struct node {
    int x, y;
    node(int _x = -1, int _y = -1): x(_x), y(_y) { }
}Max[maxn][17];

bool cmp(int x, int y) { return x > y; }
int t[5];
node merge(const node &a, const node &b) {
    t[0] = a.x, t[1] = a.y;
    t[4] = b.x, t[3] = b.y;
    sort(t, t + 5, cmp);
    unique(t, t + 5);
    return (node){t[0], t[1]};
}

int fa[maxn][17], d[maxn];

void dfs(int now, int f) {
    fa[now][0] = f, d[now] = d[f] + 1;
    for (int i = 1; i <= 16; i++) {
        fa[now][i] = fa[fa[now][i - 1]][i - 1];
        Max[now][i] = merge(Max[now][i - 1], Max[fa[now][i - 1]][i - 1]);
    }
    for (int i = 0; i < g[now].size(); i++) {
        int son = g[now][i].first, ds = g[now][i].second;
        if (son == f) continue;
        Max[son][0] = (node){ds, -1};
        dfs(son, now);
    }
}

node find(int x, int y) {
    if (d[x] < d[y]) swap(x, y);
    node res = (node){-1, -1};
    for (int i = 16; ~i; i--)
        if (d[fa[x][i]] >= d[y]) res = merge(res, Max[x][i]), x = fa[x][i];
    if (x == y) return res;
    for (int i = 16; ~i; i--)
        if (fa[x][i] != fa[y][i]) {
            res = merge(res, Max[x][i]);
            res = merge(res, Max[y][i]);
            x = fa[x][i], y = fa[y][i];
        }
    res = merge(res, Max[x][0]);
    res = merge(res, Max[y][0]);
    return res;
}

int main() {
#ifdef xunzhen
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
#endif

    n = read(); m = read();
    for (int i = 1; i <= m; i++) {
        int x = read(), y = read(), z = read();
        e[i] = (edge){x, y, z};
    }

    LL sum = MST :: kruskal(), ans = inf;

    t[4] = -1, dfs(1, 0);

    for (int i = 1; i <= m; i++)
        if (!b[i]) {
            node res = find(e[i].x, e[i].y);
            if (e[i].z > res.x) ans = min(ans, sum + e[i].z - res.x);
            else if (e[i].z > res.y) ans = min(ans, sum + e[i].z - res.y);
        }

    printf("%lld\n", ans);

    return 0;
}

Summary

這道題的思路不是很難,結果在碼碼的過程中出現了很多SB錯誤,調了我一個小時才調出來...

BZOJ1977[BJOI2010] 次小生成樹