1. 程式人生 > >【MST】P2323 [HNOI2006]公路修建問題.md

【MST】P2323 [HNOI2006]公路修建問題.md

一行 line pre main nio define cast 克魯斯 ons

Description

給定 \(n\) 個點 \(m - 1\) 條無向邊,每條邊有兩種邊權,貴一點的和便宜一點的。要求至少選擇 \(k\) 條貴邊使得圖聯通且花費最大的邊權值最小。

Input

第一行是三個整數 \(n,m,k\)

下面 \(m - 1\) 行每行描述一條邊。

Output

輸出最小花費與方案。

Hint

\(1~\leq~n~\leq~10000,1~\leq~m~\leq~20000\) ,邊權 \(\leq~30000\)

Solution

兩種做法。

首先題面已經非常明顯的提示二分答案,於是我們二分最大花費,然後對兩種邊分別跑克魯斯卡爾即可。由於較貴的邊有限制,所以我們優先跑較貴的邊的克魯爾卡爾,跑完再跑便宜的邊。

這樣做的正確性是因為對於一條合法的兩個不同的聯通塊之間的邊,他被枚舉到的時候是一定會被選擇的。

註意到克魯斯卡爾的復雜度為排序 \(O(m \log m)\),主算法 \(O(m~\alpha(n))\),本題不需要排序,但是一共跑了 \(O(\log c)\) 次主算法,所以總體時間復雜度為 \(O(m~\alpha(n)~\log c)\)

看起來那個 \(O(\alpha(n)~\times~\log c)\) 非常不優美,直覺告訴我這一部分是可以被去掉的,於是我們考慮優化上述算法。

註意到我們在上述算法中對邊權是否排序是不影響答案的,所以我們不妨對貴的邊進行排序。然後我們發現對貴的邊跑 MST 的時候等價於選擇出至少 \(k\)

條不同聯通塊之間的邊。於是我們直接貪心的選出 \(k\) 條邊權最小的且能構成合法聯通塊的邊。即對貴的邊跑 MST 直到選出 \(k\) 條邊。

我們發現剩下的邊選貴的還是便宜的是無所謂的,於是我們將他們混在一起排序,再對剩下的邊跑一遍 MST 即可得答案。

這樣做我們一共進行了 \(O(1)\) 次克魯斯卡爾 \(O(m~\alpha(n))\) 的主算法,排序復雜度為 \(O(m~\log m)\)。由於 於是總復雜度 \(O(m~(\log m + \alpha(n))\)。成功的去掉了乘積

Code

\(O(m~\alpha(n)~\log c)\):

#include <cstdio>
#include <vector>
#include <algorithm>
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    int top=0;
    do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 10010;
const int maxm = 20010;

struct Edge {
    int from, to, v, id;
    
    inline void build(int x, int y, int z, int w) {
        from = x; to = y; v = z; id = w;
    }

    inline bool operator<(const Edge &_others) const {
        return this->v < _others.v;
    }
};
Edge com[maxm], expen[maxm];

int n, m, k, dn;
std::vector< std::pair<int, int> > MU;
int ufs[maxn], rk[maxn];

bool check(int, bool);
int find(ci);
void  unionn(int, int);

int main() {
    freopen("1.in", "r", stdin);
    qr(n); qr(k); qr(m); dn = n - 1;
    for (int i = 1, a, b, c; i < m; ++i) {
        a = b = c = 0; qr(a); qr(b); qr(c);
        expen[i].build(a, b, c, i);
        c = 0; qr(c);
        com[i].build(a, b, c, i);
    }
    int l = 1, r = 30000, mid, ans = 0; 
    while (l <= r) {
        mid = (l + r) >> 1;
        if (check(mid, false)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    qw(ans, '\n', true); check(ans, true); std::sort(MU.begin(), MU.end());
    for (int i = 0; i < dn; ++i) {
        qw(MU[i].first, ' ', true); qw(MU[i].second, '\n', true);
    }
    return 0;
}

bool check(int x, bool rec) {
    for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
    int cnt = 0;
    for (int i = 1; i < m; ++i) if (expen[i].v <= x) {
        int fa = find(expen[i].from), fb = find(expen[i].to);
        if (fa == fb) continue;
        unionn(fa, fb);
        if (rec) MU.push_back(std::make_pair(expen[i].id, 1));
        ++cnt;
    }
    if (cnt < k) return false;
    for (int i = 1; i < m; ++i) if (com[i].v <= x) {
        int fa = find(com[i].from), fb = find(com[i].to);
        if (fa == fb) continue;
        unionn(fa, fb);
        if (rec) MU.push_back(std::make_pair(com[i].id, 2));
        ++cnt;
        if (cnt == dn) return true;
    }
    return cnt == dn;
}

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

void unionn(int fa, int fb) {
    if (rk[fa] < rk[fb]) ufs[fa] = fb;
    else if (rk[fb] < rk[fa]) ufs[fb] = fa;
    else ufs[fb] = fa, ++rk[fa];
}

\(O(m (\log m + \alpha(n)))\):

#include <cstdio>
#include <vector>
#include <algorithm>
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    int top=0;
    do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 20010;
const int maxm = 40010;

struct Edge {
    int from, to, v, id, tp;
    
    inline void build(int x, int y, int z, int w, int u) {
        from = x; to = y; v = z; id = w; tp = u;
    }

    inline bool operator<(const Edge &_others) const {
        return this->v < _others.v;
    }
};
Edge expen[maxn], tmp, edge[maxm];
std::vector< std::pair<int, int> >ans;

int n, k, m, ecnt, maxans;
int ufs[maxn], rk[maxn];

int find(int);
void unionn(int, int);
void printans();

int main() {
    freopen("1.in", "r", stdin);
    qr(n); qr(k); qr(m); int dn = n - 1;
    for (int i = 1, a, b, c; i < m; ++i) {
        a = b = c = 0; qr(a); qr(b); qr(c);
        expen[i].build(a, b, c, i, 1); c = 0; qr(c);
        edge[++ecnt].build(a, b, c, i, 2);
    }
    std::sort(expen + 1, expen + m);
    int cnt = 0;
    for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
    for (int i = 1; i < m; ++i) {
        int fa = find(expen[i].from), fb = find(expen[i].to);
        if (fa == fb) {
            edge[++ecnt] = expen[i];
        } else {
            unionn(fa, fb);
            maxans = expen[i].v;
            ans.push_back(std::make_pair(expen[i].id, 1));
            if ((++cnt) == k) break;
        }
    } 
    std::sort(edge + 1, edge + 1 + ecnt);
    for (int i = 1; cnt !=  dn; ++i) {
        int fa = find(edge[i].from), fb = find(edge[i].to);
        if (fa == fb) continue;
        unionn(fa, fb);
        maxans = std::max(maxans, edge[i].v);
        ans.push_back(std::make_pair(edge[i].id, edge[i].tp));
        ++cnt;
    }
    printans();
    return 0;
}

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

void unionn(int a, int b) {
    if (rk[a] < rk[b]) ufs[a] = b;
    else if (rk[a] > rk[b]) ufs[b] = a;
    else ufs[b] = a, ++rk[b];
}

void printans() {
    qw(maxans, '\n', true);
    std::sort(ans.begin(), ans.end());
    for (auto i : ans) {
        qw(i.first, ' ', true); qw(i.second, '\n', true);
    }
}

【MST】P2323 [HNOI2006]公路修建問題.md