Luogu-P5663-加工零件

练习题T1

题目 P5663 [CSP-J2019] 加工零件

题目描述

凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 nn 位工人,工人们从 1n1 \sim n 编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。

如果 xx 号工人想生产一个被加工到第 L(L>1)L\,(L \gt 1) 阶段的零件,则所有xx 号工人有传送带直接相连的工人,都需要生产一个被加工到第 L1L - 1 阶段的零件(但 xx 号工人自己无需生产第 L1L - 1 阶段的零件)。

如果 xx 号工人想生产一个被加工到第 11 阶段的零件,则所有xx 号工人有传送带直接相连的工人,都需要为 xx 号工人提供一个原材料。

轩轩是 11 号工人。现在给出 qq 张工单,第 ii 张工单表示编号为 aia_i 的工人想生产一个第 LiL_i 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!

输入格式

第一行三个正整数 nnmmqq,分别表示工人的数目、传送带的数目和工单的数目。

接下来 mm 行,每行两个正整数 uuvv,表示编号为 uuvv 的工人之间存在一条零件传输带。保证 uvu \neq v

接下来 qq 行,每行两个正整数 aaLL,表示编号为 aa 的工人想生产一个第 LL 阶段的零件。

输出格式

qq 行,每行一个字符串 Yes 或者 No。如果按照第 ii 张工单生产,需要编号为 1 的轩轩提供原材料,则在第 ii 行输出 Yes;否则在第 ii 行输出 No。注意输出不含引号。

思路:最短路

转化条件

不难发现,需要 11 号节点提供第 LL 阶段零件原材料的充要条件是“能通过走 LL 步到达 11 号节点”。

并且,只需要考虑一个节点的情况(单源)。

所以使用广搜~~(面搜)~~寻找最短路。

奇偶?

由于存在双向传送带,所以这个图没有方向性。

可以花费两步走一次来回,即:走 kk 步可以到达 \Rightarrowk+2k+2 步可以到达。

因此,只需要分别考虑奇数步的最短路和偶数步的最短路即可。

实现

使用 oddmin[i],evenmin[i]odd_min[i], even_min[i] 代表 1i1 \rightarrow i 奇偶步数的最短路。

iijj 相连,则:

1
2
odd_min[j] = even_min[i] + 1;
even_min[j] = odd_min[i] + 1;

同时,在搜索的时候不能仅使用一个 visvis 记录访问状态,因为这样只能求出一种情况的最短路。

80 pts RE Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// template v7
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

inline void minset(int &t, int other) {
t = min(t, other);
}

inline void maxset(int &t, int other) {
t = max(t, other);
}

const int inf = 0x7f7f7f7f;

const int N = 1e5 + 10;
const int M = 1e5 + 10;

int odd_min[N], even_min[N];
bool odd_vis[N], even_vis[N];

struct edge {
int to, next;
} edges[M];

struct node {
int from, to;
};

int head[N];
int pt = 1;
string res[] = {"No", "Yes"};

int n, m, q;

void add_edge(int from, int to) {
edge &e = edges[pt];
e.to = to;
e.next = head[from];
head[from] = pt;
pt++;
}

void bfs() {
// from 1
int p = 1;
even_min[1] = 0;
even_vis[1] = true;
queue<node> q;
for(int i = head[1]; i; i = edges[i].next) {
int from = 1, to = edges[i].to;
q.push({from, to});
}
while(q.size()) {
node n = q.front();
q.pop();
bool flag = false;
if(!odd_vis[n.to] && even_min[n.from] != inf) {
odd_min[n.to] = even_min[n.from] + 1;
odd_vis[n.to] = true;
flag = true;
}
if(!even_vis[n.to] && odd_min[n.from] != inf) {
even_min[n.to] = odd_min[n.from] + 1;
even_vis[n.to] = true;
flag = true;
}
if(flag) {
for(int i = head[n.to]; i; i = edges[i].next) {
int from = n.to, to = edges[i].to;
q.push({from, to});
}
}
}
}

int main() {
cin >> n >> m >> q;
int t1, t2;
for(int i = 1; i <= m; i++) {
cin >> t1 >> t2;
add_edge(t1, t2);
add_edge(t2, t1);
}
memset(odd_min, inf, sizeof odd_min);
memset(even_min, inf, sizeof even_min);
bfs();
for(int i = 0; i < q; i++) {
cin >> t1 >> t2;
if(t2 & 1) {
cout << res[odd_min[t1] <= t2] << endl;
} else {
cout << res[even_min[t1] <= t2] << endl;
}
}
return 0;
}

什么情况?

无向图的数组长度应该为边数的二倍,edges[M]这里写小了。

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// template v7
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

inline void minset(int &t, int other) {
t = min(t, other);
}

inline void maxset(int &t, int other) {
t = max(t, other);
}

const int inf = 0x7f7f7f7f;

const int N = 1e5 + 10;
const int M = 1e5 + 10;

int odd_min[N], even_min[N];
bool odd_vis[N], even_vis[N];

struct edge {
int to, next;
} edges[2 * M];

struct node {
int from, to;
};

int head[N];
int pt = 1;
string res[] = {"No", "Yes"};

int n, m, q;

void add_edge(int from, int to) {
edge &e = edges[pt];
e.to = to;
e.next = head[from];
head[from] = pt;
pt++;
}

void bfs() {
// from 1
int p = 1;
even_min[1] = 0;
even_vis[1] = true;
queue<node> q;
for(int i = head[1]; i; i = edges[i].next) {
int from = 1, to = edges[i].to;
q.push({from, to});
}
while(q.size()) {
node n = q.front();
q.pop();
bool flag = false;
if(!odd_vis[n.to] && even_min[n.from] != inf) {
odd_min[n.to] = even_min[n.from] + 1;
odd_vis[n.to] = true;
flag = true;
}
if(!even_vis[n.to] && odd_min[n.from] != inf) {
even_min[n.to] = odd_min[n.from] + 1;
even_vis[n.to] = true;
flag = true;
}
if(flag) {
for(int i = head[n.to]; i; i = edges[i].next) {
int from = n.to, to = edges[i].to;
q.push({from, to});
}
}
}
}

int main() {
cin >> n >> m >> q;
int t1, t2;
for(int i = 1; i <= m; i++) {
cin >> t1 >> t2;
add_edge(t1, t2);
add_edge(t2, t1);
}
memset(odd_min, inf, sizeof odd_min);
memset(even_min, inf, sizeof even_min);
bfs();
for(int i = 0; i < q; i++) {
cin >> t1 >> t2;
if(t2 & 1) {
cout << res[odd_min[t1] <= t2] << endl;
} else {
cout << res[even_min[t1] <= t2] << endl;
}
}
return 0;
}