题意
一个有 n n n 个点, m m m 条边的无向图,一个点想要得到一个 L L L 阶段的数,则与它有直接连边的点都要得到一个 L − 1 L-1 L−1 阶段的数,如果一个点要得到一个第一阶段的数,则与它有直接连边的点需要给它提供一个原材料。
求: 有 T T T 组数据,每组数据输入一个 a a a 与 L L L ,计算点 a a a 需要一个 L L L 阶段的数,点 1 是否需要提供原材料。
题解
如果将从 1 1 1 ~ a a a 的最短路径看作是 c n t cnt cnt 的话,则可以进行分类讨论:
分类一: L = c n t L = cnt L=cnt
不难发现,在这种情况下,轮到 1 时正好需要提供一个原材料
分类二: L > c n t L > cnt L>cnt ,在这一分类下情况相对复杂
第一种情况: L L L 是偶数
在这种情况下又可以进行分类,第一种是 c n t cnt cnt 为奇数,第二种是 c n t cnt cnt 为偶数。
观察上图不难发现:
当 c n t cnt cnt 与 L L L 均为偶数时,1 需要提供一个原材料
当 c n t cnt cnt 为奇数, L L L 为偶数时,1 不需要提供原材料
第二种情况: L L L 为奇数
在这种情况下依旧需要分类,第一种是 c n t cnt cnt 为奇数,第二种是 c n t cnt cnt 为偶数。观察上图可知:
当 c n t cnt cnt 与 L L L 均为奇数时,1 需要提供一个原材料
当 c n t cnt cnt 为偶数,而 L L L 为奇数时,1 不需要提供原材料
分类三: L < c n t L < cnt L<cnt
很容易看出, 在这种情况下,还没有到1就已经有点提供了原材料,1 不需要提供原材料
(关于其它点是否也需要提供原材料暂不考虑,因为题目只要求 1 是否要提供原材料)
将以上三种分类进行一定的整合后,我们可以得到三条结论:
- L = c n t L = cnt L=cnt 时,必定需要提供原材料
- L > c n t L > cnt L>cnt 时,如果 c n t cnt cnt 与 L L L 的奇偶性相同,需要提供原材料;反之,则无需提供原材料
- L < c n t L < cnt L<cnt 时,必定无需提供原材料
得到这三条结论之后,解决这道题的思路就非常明显了:
只要求出从 1 到 a a a 的最短路径,判断它与 L L L 的大小关系奇偶关系,根据上述结论判断是否需要提供原材料。
定义两个数组 o d d odd odd 与 e v e n even even ,
o d d [ i ] odd[i] odd[i] 表示从 1 到 i i i 的奇数(步数)最短路径
e v e n [ i ] even[i] even[i] 表示从 1 到 i i i 的偶数最短路径
注意:需要将这两个数组都初始化成 INF,之后便于判断。
接下来根据题目意思正常建图,通过广度优先搜索得到从 1 到 a a a 的奇数最短路径和偶数最短路径(其中一定有一个值为INF),接下来进行判断:
当 L L L 为奇数时:
如果 o d d [ i ] odd[i] odd[i] <= L L L (如果 o d d [ i ] > L odd[i] > L odd[i]>L ,则 说明不存在奇数最短路径,或者此最短路径大于 L L L ,根据结论3必定不需要提供原材料), 需要提供原材料,否则不需要提供原材料。
当 L L L 为偶数时:
如果 e v e n [ i ] even[i] even[i] <= L L L (如果 e v e n [ i ] > L even[i] > L even[i]>L ,则 说明不存在偶数最短路径,或者此最短路径大于 L L L ,根据结论3必定不需要提供原材料), 需要提供原材料,否则不需要提供原材料。
代码实现:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int even[100005];
int odd[100005];
vector<int> g[100005];
struct Node{
int x;
int step;
};
queue<Node> q;
int main() {
memset(even, INF, sizeof(even));
memset(odd, INF, sizeof(odd));
int n, m, T;
cin >> n >> m >> T;
for (int i = 1; i <= m; i ++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
q.push({1, 0});
while (!q.empty()) {
Node cur = q.front();
q.pop();
int x = cur.x;
int step = cur.step;
for (auto i : g[x]) {
if (step % 2 == 0 and odd[i] == INF) {
//step 并非表示到点 x 的步数,step + 1才表示,在判断时需要转化一下
q.push({i, step + 1});
odd[i] = step + 1;
}
if (step % 2 == 1 and even[i] == INF) {
q.push({i, step + 1});
even[i] = step + 1;
}
}
}
while (T --) {
int a, l;
cin >> a >> l;
if (l & 1) odd[a] <= l ? cout << "Yes" << endl : cout << "No" << endl;
else even[a] <= l ? cout << "Yes" << endl : cout << "No" << endl;
}
//使用三目运算符,能更方便地完成判断
return 0;
}