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;
}
posted @ 2022-09-24 19:03  MistZero  阅读(53)  评论(0编辑  收藏  举报