[CSP-J2 2019]加工零件 题解
题目地址:洛谷:P5663 加工零件 – 洛谷 | 计算机科学教育新生态
题目描述
凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 $n$ 位工人,工人们从 $1 \sim n$ 编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。
如果 $x$ 号工人想生产一个被加工到第 $L \ (L \gt 1)$ 阶段的零件,则所有与 $x$ 号工人有传送带直接相连的工人,都需要生产一个被加工到第 $L – 1$ 阶段的零件(但 $x$ 号工人自己无需生产第 $L – 1$ 阶段的零件)。
如果 $x$ 号工人想生产一个被加工到第 $1$ 阶段的零件,则所有与 $x$ 号工人有传送带直接相连的工人,都需要为 $x$ 号工人提供一个原材料。
轩轩是 $1$ 号工人。现在给出 $q$ 张工单,第 $i$ 张工单表示编号为 $a_i$ 的工人想生产一个第 $L_i$ 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!
题意简述
给定一个 $n$ 个点 $m$ 条边的无向图,如果给 $x$ 号点标注 $L$ ,则与 $x$ 点相邻的点都会被标注 $L-1$ ,以此类推。有 $q$ 个询问,每个询问问给 $a$ 点标注 $L$ , $1$ 点是否会被标注 $0$ 。
输入输出格式
输入格式:
第一行三个正整数 $n$,$m$ 和 $q$,分别表示工人的数目、传送带的数目和工单的数目。
接下来 $m$ 行,每行两个正整数 $u$ 和 $v$,表示编号为 $u$ 和 $v$ 的工人之间存在一条零件传输带。保证 $u \neq v$。
接下来 $q$ 行,每行两个正整数 $a$ 和 $L$,表示编号为 $a$ 的工人想生产一个第 $L$ 阶段的零件。
输出格式:
共 $q$ 行,每行一个字符串 Yes 或者 No。如果按照第 $i$ 张工单生产,需要编号为 $1$ 的轩轩提供原材料,则在第 $i$ 行输出 Yes;否则在第 $i$ 行输出 No。注意输出不含引号。
输入输出样例
样例 #1
输入样例 #1:
3 2 6 1 2 2 3 1 1 2 1 3 1 1 2 2 2 3 2
输出样例 #1:
No Yes No Yes No Yes
样例 #2
输入样例 #2:
5 5 5 1 2 2 3 3 4 4 5 1 5 1 1 1 2 1 3 1 4 1 5
输出样例 #2:
No Yes No Yes Yes
数据范围
共 20 个测试点。
$1 \leq u, v, a \leq n$。
测试点 $1 \sim 4$ ,$1 \leq n, m \leq 1000$,$q = 3$,$L = 1$。
测试点 $5 \sim 8$,$1 \leq n, m \leq 1000$,$q = 3$,$1 \leq L \leq 10$。
测试点 $9 \sim 12$,$1 \leq n, m, L \leq 1000$,$1 \leq q \leq 100$。
测试点 $13 \sim 16$,$1 \leq n, m, L \leq 1000$,$1 \leq q \leq 10^5$。
测试点 $17 \sim 20$,$1 \leq n, m, q \leq 10^5$,$1 \leq L \leq 10^9$。
题解
容易发现,需要提供原材料的点到询问点一定存在长度为 $L$ 的路径,这里的路径可以存在重复经过边的情况。则问题转化为:$1$ 点到 $a$ 点是否存在长度为 $L$ 的路径。
由于沿着一条边走一个来回会让一条路径变长 $2$ ,如果 $1$ 到 $a$ 存在长为 $d$ 的路径,那么也就存在长为 $d + 2k \ (k \in \mathbb{N})$ 的路径。如果 $1$ 到 $a$ 长度为奇数的最短路径长为 $d_{\text{odd}}$ ,则不小于 $d_{\text{odd}}$ 的奇数长度的路径都存在,偶数以此类推。
因此,我们只需要求出 $1$ 到其他点的长为奇数/偶数的最短路径长度即可回答每个询问,如果 $L$ 是奇数且不小于 $1$ 到 $a$ 的奇数长度最短路径,则可行,偶数以此类推。
处理奇数/偶数长度最短路可以用一个 BFS 来做。从 $1$ 开始进行 BFS ,每次用队列中一个点的当前奇数/偶数长度最短路更新相邻点的偶数/奇数最短路即可。由于边长都为 $1$ ,每个点最多只会加入队列 $2$ 次,因此 BFS 的复杂度为 $O(n)$ ,单次回答复杂度为 $O(1)$ ,因此处理询问复杂度为 $O(q)$ ,总复杂度为 $O(n)$ 。
洛谷民间数据中,有一个点的 $1$ 点没有连边,因此所有的答案都为 No ,需要特判。
代码
// Code by KSkun, 2019/11
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
typedef long long LL;
typedef std::pair<int, int> PII;
inline char fgc() {
static char buf[100000], * p1 = buf, * p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
LL res = 0, neg = 1; char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = res * 10 + c - '0';
return res * neg;
}
inline char readsingle() {
char c;
while(!isgraph(c = fgc())) {}
return c;
}
const int MAXN = 100005,
INF = 0x3f3f3f3f;
int n, m, q, deg[MAXN], dise[MAXN], diso[MAXN];
bool inque[MAXN];
std::vector<int> gra[MAXN];
void bfs(int st) {
std::queue<int> q;
memset(dise, 0x3f, sizeof(dise));
memset(diso, 0x3f, sizeof(diso));
dise[st] = 0;
q.push(st); inque[st] = true;
while (!q.empty()) {
int u = q.front(); q.pop(); inque[u] = false;
for (int v : gra[u]) {
bool suc = false;
if (dise[u] + 1 < diso[v]) {
diso[v] = dise[u] + 1;
suc = true;
}
if (diso[u] + 1 < dise[v]) {
dise[v] = diso[u] + 1;
suc = true;
}
if (suc && !inque[v]) {
q.push(v); inque[v] = true;
}
}
}
}
int main() {
n = readint(); m = readint(); q = readint();
for (int i = 1, u, v; i <= m; i++) {
u = readint(); v = readint();
gra[u].push_back(v);
gra[v].push_back(u);
deg[u]++; deg[v]++;
}
bfs(1);
while (q--) {
int a = readint(), L = readint();
if (deg[1] == 0) puts("No");
else if (L % 2 == 0 && L >= dise[a]) puts("Yes");
else if (L % 2 == 1 && L >= diso[a]) puts("Yes");
else puts("No");
}
return 0;
}