题解:P9881 [EC Final 2021] Elden Ring
下文下标统一从
由于怪物是一天开始时升级,为了方便不妨令
首先我们考虑
其次是
- 如果
w\le l_i ,那么玩家无论如何也不能击杀该怪物,即区间为空集,可以令\mathrm{limit}_i=0 。 - 如果
w>l_i ,那么满足w+(t-1)\times A>l_i+(t-1)\times B 的最大的t 就是\mathrm{limit}_i 。整理得\mathrm{limit}_i=\lfloor\frac{w-l_i+1}{B-A}\rfloor+1 。
我们限制每个节点的距离小于等于
最后是
- 如果
w\le l_i ,那么满足w+(t-1)\times A>l_i+(t-1)\times B 的最小的t 就是\mathrm{limit}_i ,整理得\mathrm{limit}_i=\lfloor\frac{l_i-w}{A-B}\rfloor+2 。 - 如果
w>l_i ,那么这个怪物任意时刻都能被击杀。令\mathrm{limit}_i=1 。
我们限制每个节点的距离大于等于
我们想到如果等级不够,可以将该节点的
总时间复杂度
namespace hellolin {
static constexpr int INF = 1e9;
void solve() {
int n, m, a, b;
io.read(n, m, a, b);
vector<vector<int>> g(n);
for (int i = 0, u, v; i < m; ++i) {
io.read(u, v);
--u, --v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> l(n);
for (int &i : l) {
io.read(i);
i += b;
}
int w = l[0] - b;
if (a <= b) {
// 这里将等于和小于两种情况合并,他们的代码相似,把 limit 置 0 相当于删除某个节点
vector<int> limit(n), dist(n, INF);
dist[0] = 0;
for (int i = 1; i < n; ++i) {
if (a < b) {
limit[i] = l[i] >= w ? 0 : ((w - l[i] - 1) / (b - a) + 1);
} else {
limit[i] = l[i] >= w ? 0 : INF;
}
}
queue<int> q;
q.push(0);
while (!q.empty()) {
int u = q.front();
q.pop();
for (const int &v : g[u]) {
if (dist[u] + 1 <= limit[v] and chmin(dist[v], dist[u] + 1)) {
q.push(v);
}
}
}
io.writeln(dist[n - 1] == INF ? -1 : dist[n - 1]);
} else {
vector<int> limit(n), dist(n, INF);
dist[0] = 0;
for (int i = 1; i < n; ++i) {
limit[i] = w > l[i] ? 1 : ((l[i] - w) / (a - b) + 2);
}
using state = pair<int, int>;
priority_queue<state, vector<state>, greater<state>> q;
q.emplace(0, 0);
int current = 0;
while (!q.empty()) {
auto [_, u] = q.top();
q.pop();
if (current < dist[u]) {
// 记录总共打了多少怪,如果升级不够就重置这个节点
dist[u] = INF;
continue;
}
++current;
for (const int &v : g[u]) {
if (chmin(dist[v], max(dist[u] + 1, limit[v]))) {
// 这里取 min 时同时取 limit,就是假设可以打其他怪升级后返回打这个怪
q.emplace(dist[v], v);
}
}
}
io.writeln(dist[n - 1] == INF ? -1 : dist[n - 1]);
}
}
void main() {
int t;
for (io.read(t); t--;)
solve();
}
} // namespace hellolin