JOIG 2021/2022 F 题解

链接

题意:给定一张 n 个点,m 条边的无向图(保证没有重边、自环)。边有两种,type=1 时,经过后手中的值 1type=2 时,经过后手中的值 ÷2 下取整。给定 q 个询问以及常数 L,初始在点 1 上,每一次给定目标点 p,需要你求出最小的初始值,使得存在一种方案,使得到达 p 时该数 1。若最小初始值 >L 则输出 Large

做法:q 次 Dijkstra 不可取,但是计算过程有一定启发性。

考虑将 1p 变为 p1。那么可以将初值设为 1,对于 type=1,该值 +1;对于 type=2,该值 ×2。最后得出的一定是最小值。

像这样就只能做 q 次 Dijkstra,T 飞。

那么考虑反过来,从 1 开始走。怎么做呢?

考虑 type=2 对于答案的贡献。由于最后答案会变成 1,倒过来推就是每一个 type=2 会对其后继中 type=1 的边产生贡献。

那么就想到维护一个三元组进行 Dijkstra。转移的状态包括当前的答案、当前所在的点以及当前经过的 type=2 数量。

那么转移就很显然了。

当前如果为 type=1,那么当前的边权要乘上 2k,其中 k 表示经过的 type=2 的边数。否则就需要更新 k,接着转移就行了。注意到这个时候有 k 的变化,所以需要分层图。由题面容易得到层数为 log 级别,复杂度便保证为 Mlog2N 了。

细节在于最后答案为 1,所以初值为 1,每一次遇到 type=2 加上 2kk 初始为 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 @   MistZero  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示