TIOJ 2125:殿壬與情人節

TIOJ 2125殿壬與情人節


題目大意:有一棵 $n$ 點的樹,有 $m$ 個藍點與 $m$ 個紅點,有 $m$ 個守衛,一個守衛掌管一個藍點與紅點,可挑選一點當休息室,要從休息室出發巡邏完兩點後回到休息室,需要花費電力照亮手電筒,每走一單位距離即用到一單位電力,回到休息室可將電力充到電容量。每個守衛的手電筒都有相同的電容量,問在適當的休息室及藍點紅點分配下,電容量最低可以是多少。

解法:首先看只有一藍點與紅點下的電容量:可以找他們的 $LCA$,分別二分搜藍點到休息室,紅點到休息室的兩段距離最大值最小可以是多少。之後我們算出來每種不同配對狀況的電容量。之後對電容量二分搜,如果 $Dis[i][j]$ 小於等於電容量,就可以連一條 $i,j$ 的邊,做二分圖最大匹配看能不能湊成 $m$ 對。最後即可得到答案。

ps. 這題是我用 rand(1001,2242) 找到的,然後明天都要初選了我還在寫 Blog...

$\text{Code:}$

#pragma GCC optimize("O3")
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define Waimai ios::sync_with_stdio(false)
#define FOR(x,a,b) for(int x=a;x<=b;x++)
#define pb emplace_back
#define F first
#define S second

const int SIZE = 5e5 + 5;
const int MSIZ = 305;
const int H = __lg (SIZE) + 1;
const int INF = 1e15;

int n, m;
vector<pair<int, int>> adj[SIZE];
int h[SIZE], to[SIZE][H + 1], len[SIZE][H + 1];
int p1[MSIZ], p2[MSIZ], match[MSIZ], Dis[MSIZ][MSIZ];
vector<int> madj[MSIZ];
bool vs[MSIZ];

void dfs (int pos, int fa) {
    for (auto [np, w] : adj[pos]) {
        if (np != fa) {
            h[np] = h[pos] + 1;
            to[np][0] = pos;
            len[np][0] = w;
            dfs (np, pos);
        }
    }
}

int jump (int pos, int k, int &ans) {int cnt = 0; while (k) {if (k & 1) {ans += len[pos][cnt]; pos = to[pos][cnt];} cnt++; k >>= 1;} return pos;}
int lca (int a, int b, int &ans) {
    if (h[a] < h[b]) {
        swap (a, b);
    }
    a = jump (a, h[a] - h[b], ans);
    if (a == b) {
        return a;
    }
    for (int i = H; i >= 0; i--) {
        if (to[a][i] != to[b][i]) {
            ans += len[a][i] + len[b][i];
            a = to[a][i];
            b = to[b][i];
        }
    }
    ans += len[a][0] + len[b][0];
    return to[a][0];
}

int cal (int a, int b) {
    int _, root = lca (a, b, _), l, r, adis = 0, bdis = 0, re = INF;
    jump (a, h[a] - h[root], adis);
    jump (b, h[b] - h[root], bdis);
    l = 0, r = h[a] - h[root];
    while (l < r) {
        int mid = (l + r) / 2, ldis = 0, rdis = bdis, d1, d2, t;
        t = jump (a, mid, ldis);
        rdis += adis - ldis;
        d1 = max (ldis, rdis);
        ldis += len[t][0];
        rdis -= len[t][0];
        d2 = max (ldis, rdis);
        if (d1 < d2) {
            r = mid;
            re = min (re, d1);
        } else {
            l = mid + 1;
            re = min (re, d2);
        }
    }
    l = 0, r = h[b] - h[root];
    while (l < r) {
        int mid = (l + r) / 2, ldis = 0, rdis = adis, d1, d2, t;
        t = jump (b, mid, ldis);
        rdis += bdis - ldis;
        d1 = max (ldis, rdis);
        ldis += len[t][0];
        rdis -= len[t][0];
        d2 = max (ldis, rdis);
        if (d1 < d2) {
            r = mid;
            re = min (re, d1);
        } else {
            l = mid + 1;
            re = min (re, d2);
        }
    }
    return re;
}

bool bimatch (int pos) {
    for (int np : madj[pos]) {
        if (!vs[np]) {
            vs[np] = 1;
            if (match[np] == 0 || bimatch (match[np])) {
                match[np] = pos;
                return 1;
            }
        }
    }
    return 0;
}

bool ok (int x) {
    fill (madj, madj + m + 1, vector<int>());
    fill (match, match + m + 1, 0);
    FOR (i, 1, m) FOR (j, 1, m) {
        if (Dis[i][j] <= x) {
            madj[i].pb (j);
        }
    }
    int cnt = 0;
    FOR (i, 1, m) {
        fill (vs, vs + m + 1, 0);
        cnt += bimatch (i);
    }
    return cnt == m;
}

void solve() {
    cin >> n >> m;
    FOR (i, 2, n) {
        int a, b, w;
        cin >> a >> b >> w;
        adj[a].pb (b, w);
        adj[b].pb (a, w);
    }
    dfs (1, 1);
    FOR (i, 1, H) FOR (j, 1, n) {
        int fa = to[j][i - 1];
        to[j][i] = to[fa][i - 1];
        len[j][i] = len[j][i - 1] + len[fa][i - 1];
    }
    FOR (i, 1, m) cin >> p1[i];
    FOR (i, 1, m) cin >> p2[i];
    FOR (i, 1, m) FOR (j, 1, m) {
        int a = p1[i], b = p2[j];
        Dis[i][j] = 2 * cal (a, b);
    }
    int l = 0, r = INF;
    while (l < r) {
        int mid = (l + r) / 2;
        if (ok (mid)) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    cout << l << '\n';
}

int32_t main() {
    Waimai;
    solve();
}

我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!

留言

熱門文章