P11225 [COTS 2019] 疏散 Sklonište 题解 二分答案 + Hall定理 + 最短路 + 高维前缀和

题目链接:https://www.luogu.com.cn/problem/P11225

前置知识:

然后就可以解决这题了。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5, maxk = 18;

int n, m, K, A[maxk], S[maxk];
bool vis[maxn];
struct Edge {
    int v, w;
};
vector<Edge> g[maxn];
long long dis[maxk][maxn], sz1[(1<<17)+5], sz2[(1<<17)+5];

struct Node {
    int u;
    long long dis;

    bool operator < (const Node &b) const {
        return dis > b.dis;
    }
};

void init() {
    memset(dis, -1, sizeof dis);
}

void dijkstra(int p) {
    int s = A[p];
    priority_queue<Node> que;
    fill(vis+1, vis+n+1, false);
    dis[p][s] = 0;
    que.push({s, 0});
    while (!que.empty()) {
        auto [u, _dis] = que.top();
        que.pop();
        if (vis[u])
            continue;
        vis[u] = true;
        dis[p][u] = _dis;
        for (auto [v, w] : g[u]) {
            if (vis[v])
                continue;
            que.push({v, _dis + w});
        }
    }
}

bool check(long long B) {
    memset(sz2, 0, sizeof sz2);
    for (int i = 1; i <= n; i++) {
        int s = 0;
        for (int j = 0; j < K; j++)
            if (dis[j][i] <= B)
                s |= (1 << j);
        sz2[s]++;
    }
    for (int i = 0; i < K; i++) {
        for (int s = 0; s < (1<<K); s++) {
            if ((s >> i) & 1)
                sz2[s] += sz2[s^(1<<i)];
        }
    }
    for (int s = 0; s < (1<<K); s++) {
        if (sz1[s] < sz2[s])
            return false;
    }
    return true;
}

int main() {
    scanf("%d%d%d", &n, &m, &K);
    for (int i = 0, u, v, w; i < m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    for (int i = 0; i < K; i++)
        scanf("%d%d", A+i, S+i);
    init();
    for (int i = 0; i < K; i++)
        dijkstra(i);

    for (int i = 0; i < K; i++)
        sz1[1<<i] = S[i];
    for (int s = 1; s < (1<<K); s++) {
        if (__builtin_popcount(s) == 1)
            continue;
        int x = s & -s;
        sz1[s] = sz1[s ^ x] + sz1[x];
    }

    long long l = 0, r = 1e14, res;
    while (l <= r) {
        long long mid = l + r >> 1;
        if (check(mid)) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%lld\n", res);

    return 0;
}
posted @ 2026-03-17 16:44  quanjun  阅读(2)  评论(0)    收藏  举报