P11225 [COTS 2019] 疏散 Sklonište 题解 二分答案 + Hall定理 + 最短路 + 高维前缀和
题目链接:https://www.luogu.com.cn/problem/P11225
前置知识:
- Hall 婚姻定理:可以问一下豆包(我是问豆包理解的)
- 高维前缀和,可以通过以下两道例题理解:
然后就可以解决这题了。
示例程序:
#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;
}
浙公网安备 33010602011771号