题解:P5663 [CSP-J2019] 加工零件

· · 题解

## 题目大意 给定一个无向图,图中每个顶点都可以生产邻接点上一个阶段的零件。给点 $q$ 次询问,第 $i$ 次询问顶点 $a_i$ 生产 $L$ 阶段的零件时顶点 $1$ 是否能够提供原材料。 ## 解题思路 可以将问题转化为从顶点 $1$ 出发走 $L$ 步是否能够停在顶点 $a_i$ ,可以重复走走过的顶点。 对下面左侧图进行分析,我们从顶点 $1$ 出发到达顶点 $2$,可以发现如果步长为 $1,3,4,5,6,7$ 等都可以到达,但是步长为 $2$ 时无法到达。 如果走红色的路径从 $1$ 到 $2$,该路径长度为 $1$,那么 $3,5,6$ 等更大的奇数步长都能到达。因为大于 $1$ 的奇数数步长我们可以通过重复路径 $2-1-2$ 最终能够停留在目标顶点。如果走蓝色的路径从 $1$ 到 $2$,该路径长度为 $4$,那么 $6,8,10$ 等更大的偶数步长同样也能停留在目标顶点。 可将问题转换为求奇数步和偶数步的分层图最短路径问题。 ![](https://cdn.luogu.com.cn/upload/image_hosting/gse319hl.png) ### 解法一:分层建图 如上中间原图,我们可以采用分层建图方式,将原图拆分为 $2$ 层建图。第一层为偶数层,第二层为奇数层,连边时选择偶层连奇层,奇层连偶层。 由于是无权图可以跑 BFS 获取顶点 $1$ 到偶终点编号 $n$ 或奇终点编号 $2n$ 的最短路。根据 $L$ 的奇偶性选择判断 $L$ 是否大于等于该条最短路的长度,如果是则输出 `Yes` 否则输出 `No`。 ```cpp #include<bits/stdc++.h> using namespace std; const int N = 100005; vector<int > G[N << 1]; int n, m, q, x, y, L, dis[N << 1]; void bfs(int st); int main() { cin >> n >> m >> q; for (int i = 1; i <= m; i++) { cin >> x >> y; G[x].push_back(y + n); //分层建图 G[y + n].push_back(x); G[y].push_back(x + n); G[x + n].push_back(y); } bfs(1); while (q--) { cin >> x >> L; if (G[x].size() == 0) cout << "No\n"; else { int res = dis[x + (L & 1) * n]; if (res <= L) cout << "Yes\n"; else cout << "No\n"; } } return 0; } void bfs(int st) { memset(dis, 0x3f, sizeof dis); queue<int> q; q.push(st); dis[st] = 0; while (q.size()) { int hx = q.front(); q.pop(); for (auto to : G[hx]) { if (dis[to] > dis[hx] + 1) { dis[to] = dis[hx] + 1; q.push(to); } } } } ``` ### 解法二:拆分点权 本质上与解法一相同,不过不用去建分层图而是将点权拆分为两份。在计算走到当前顶点的最短路时,偶距离由邻接点的奇距离更新,同样奇距离由偶距离更新。 ![](https://cdn.luogu.com.cn/upload/image_hosting/j6onuoxl.png) ```cpp #include<bits/stdc++.h> using namespace std; const int N = 100005; vector<int> G[N]; int n, m, q, x, y, L, dis[N][2], vis[N]; void bfs(int st) { queue<int> q; q.push(st); memset(dis, 0x3f, sizeof dis); dis[st][0] = 0; vis[st] = 1; //vis标记是否在队列中,优化避免一个顶点重复入队 while (q.size()) { int hx = q.front(); q.pop(); vis[hx] = 0; for (auto to : G[hx]) { //奇偶最短路交替更新 if (dis[to][0] > dis[hx][1] + 1) { dis[to][0] = dis[hx][1] + 1; if (!vis[to]) q.push(to), vis[to] = 1; } if (dis[to][1] > dis[hx][0] + 1) { dis[to][1] = dis[hx][0] + 1; if (!vis[to]) q.push(to), vis[to] = 1; } } } } int main() { cin >> n >> m >> q; for (int i = 1; i <= m; i++) { cin >> x >> y; G[x].push_back(y); //无向图双向存图 G[y].push_back(x); } bfs(1); while (q--) { cin >> x >> L; if (G[x].size() == 0) cout << "No\n"; else { int res = dis[x][L & 1]; if (res <= L) cout << "Yes\n"; else cout << "No\n"; } } return 0; } ```