【题解】凯凯的神奇零件工厂(最短路 + 奇偶层数 审核通过

Wind_Rises 砂糖老师 2025-11-23 13:52:39 2

🧩 题意解析

工厂有 n 个工人,某些工人之间有双向传送带。
如果编号为 x 的工人要生产一个 第 L 阶段 的零件:

  • 当 L = 1:所有与 x 相邻的工人必须给他提供“原材料”
  • 当 L > 1:所有与 x 相邻的工人必须生产 L−1 阶段 的零件(递归)

1 号工人(轩轩)想知道:
对于每张工单 (a, L),他是否需要被迫提供原材料?

最终我们需要回答 Q 个查询:“Yes / No”。


🔍 问题转化

若想知道“工人 a 的第 L 阶段是否会需要 1 号工人提供原材料”,需要理解:

一个工人需要原材料的时机:

他生产 第 1 阶段 的零件时。

L 阶段如何递减?

从 a 开始,每条边都会使阶段数 L 减 1,直到减到 1。

因此:
轩轩是否会提供原材料 = 是否存在从 a 到 1 的路径,使得路径长度 <= L 且 (L − 路径长度) = 1

化简形式:

路径长度 d == L - 1

markdown 复制代码

也就是说:
从 1 到 a 的最短路径长度的奇偶性与 L 的奇偶性必须一致,并且距离 ≤ L。


🌟 关键结论(最重要)

我们只需要:

  • 1 号工人出发
  • 计算所有点到 1 的最短距离,并且要区分 奇数步偶数步

定义:

  • dis[x][0] = 到 x 的“偶数步”最短距离
  • dis[x][1] = 到 x 的“奇数步”最短距离

查询时判断:

如果 dis[a][L % 2] <= L,则轩轩需要提供原材料(Yes) 否则 No

yaml 复制代码


🚀 为什么只需要 BFS?

所有边权都是 1。
但是需要区分“奇偶层数”,于是我们让每个点有两种状态:

  • 到达该点走了偶数条边
  • 到达该点走了奇数条边

因此 dis 数组大小是 n × 2。

算法复杂度:

O(n + m + q)

yaml 复制代码

完全满足要求。


🧠 证明(简述)

工人 a 想生产第 L 阶段 → 必须沿着图的边递减 L 次直到成为 1 阶段。
最终到达某工人 k 时,需要提供原材料的条件是:

k 与 a 的距离 = L - 1

复制代码

特别地,如果最终 k = 1(轩轩),则说明:

从 a 到 1 的距离 d = L - 1 → d <= L 且 (d % 2 == L % 2)

这与我们的 dis 数组判断方式一致。


✅ AC 代码(可直接使用)

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

const int MAXN = 100005;

int n, m, q;
vector<int> g[MAXN];
int dis[MAXN][2];  // dis[x][0] = 偶数步最短路, dis[x][1] = 奇数步最短路

void bfs(int s) {
    queue<int> que;
    memset(dis, 0x3f, sizeof(dis));
    dis[s][0] = 0;    // 从 1 号工人开始,走 0 条边(偶数)
    que.push(s);

    while (!que.empty()) {
        int u = que.front();
        que.pop();

        for (int v : g[u]) {
            // 当前从 u 到 v 是一条边(奇偶互换)
            if (dis[v][1] > dis[u][0] + 1) {
                dis[v][1] = dis[u][0] + 1;
                que.push(v);
            }
            if (dis[v][0] > dis[u][1] + 1) {
                dis[v][0] = dis[u][1] + 1;
                que.push(v);
            }
        }
    }
}

int main() {
    scanf("%d %d %d", &n, &m, &q);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }

    bfs(1);

    while (q--) {
        int a, L;
        scanf("%d %d", &a, &L);

        if (dis[a][L & 1] <= L)
            printf("Yes\n");
        else
            printf("No\n");
    }

    return 0;
}
'''
{{ vote && vote.total.up }}