JOIG 2021/2022 F 题解
题意:给定一张 \(n\) 个点,\(m\) 条边的无向图(保证没有重边、自环)。边有两种,\(type=1\) 时,经过后手中的值 \(-1\);\(type=2\) 时,经过后手中的值 \(\div2\) 下取整。给定 \(q\) 个询问以及常数 \(L\),初始在点 \(1\) 上,每一次给定目标点 \(p\),需要你求出最小的初始值,使得存在一种方案,使得到达 \(p\) 时该数 \(\ge1\)。若最小初始值 \(>L\) 则输出 Large
。
做法:\(q\) 次 Dijkstra 不可取,但是计算过程有一定启发性。
考虑将 \(1 \rightarrow p\) 变为 \(p \rightarrow 1\)。那么可以将初值设为 \(1\),对于 \(type=1\),该值 \(+1\);对于 \(type=2\),该值 \(\times2\)。最后得出的一定是最小值。
像这样就只能做 \(q\) 次 Dijkstra,T 飞。
那么考虑反过来,从 \(1\) 开始走。怎么做呢?
考虑 \(type=2\) 对于答案的贡献。由于最后答案会变成 \(1\),倒过来推就是每一个 \(type=2\) 会对其后继中 \(type=1\) 的边产生贡献。
那么就想到维护一个三元组进行 Dijkstra。转移的状态包括当前的答案、当前所在的点以及当前经过的 \(type=2\) 数量。
那么转移就很显然了。
当前如果为 \(type=1\),那么当前的边权要乘上 \(2^k\),其中 \(k\) 表示经过的 \(type=2\) 的边数。否则就需要更新 \(k\),接着转移就行了。注意到这个时候有 \(k\) 的变化,所以需要分层图。由题面容易得到层数为 \(\log\) 级别,复杂度便保证为 \(M \log^2 N\) 了。
细节在于最后答案为 \(1\),所以初值为 \(1\),每一次遇到 \(type=2\) 加上 \(2^k\)(\(k\) 初始为 \(0\)),随后更新 \(k\),这样就能保证这个 \(1\) 的贡献也能算上去。
代码如下。
#include <bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &x) {
x = 0; char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
}
const int N = 2e5 + 10;
int n, m, q, l, cnt, bin[31], head[N], dis[N][31];
struct Node {
int val, cur, pw;
inline bool operator <(const Node &X) const {
return val > X.val;
}
};
priority_queue <Node> que;
struct Edge { int to, nxt, col; } e[N << 1];
inline void addEdge(int u, int v, int col) {
e[++cnt].to = v, e[cnt].nxt = head[u];
e[cnt].col = col, head[u] = cnt; return ;
}
inline void dijkstra() {
que.push((Node) {1, 1, 0});
memset(dis, 0x3f, sizeof(dis)); dis[1][0] = 1;
while (!que.empty()) {
Node awa = que.top(); que.pop();
int cur = awa.cur, pw = awa.pw;
for (int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to, col = e[i].col;
if (col == 1 && dis[to][pw] > dis[cur][pw] + bin[pw]) {
dis[to][pw] = dis[cur][pw] + bin[pw];
if (dis[to][pw] <= l) que.push((Node) {dis[to][pw], to, pw});
} else if (col == 2 && dis[to][pw + 1] > dis[cur][pw] + bin[pw]) {
dis[to][pw + 1] = dis[cur][pw] + bin[pw];
if (dis[to][pw + 1] <= l) que.push((Node) {dis[to][pw + 1], to, pw + 1});
}
}
}
return ;
}
int main() {
read(n), read(m), read(q), read(l);
for (int u, v, col; m--; ) {
read(u), read(v), read(col);
addEdge(u, v, col), addEdge(v, u, col);
}
bin[0] = 1; for (int i = 1; i < 31; ++i) bin[i] = bin[i - 1] << 1;
dijkstra();
while (q--) {
int p; read(p); int curans = 2e9;
for (int i = 0; i < 31; ++i) curans = min(curans, dis[p][i]);
if (curans > l) puts("Large");
else printf("%d\n", curans);
}
return 0;
}