题目描述
凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 (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) 和 (q),分别表示工人的数目、传送带的数目和工单的数目。
接下来 (m) 行,每行两个正整数 (u) 和 (v),表示编号为 (u) 和 (v) 的工人之间存在一条零件传输带。保证 (u eq v)。
接下来 (q) 行,每行两个正整数 (a) 和 (L),表示编号为 (a) 的工人想生产一个第 (L) 阶段的零件。
输出格式
共 (q) 行,每行一个字符串 (Yes) 或者 (No)。如果按照第 (i) 张工单生产,需要编号为 1 的轩轩提供原材料,则在第 (i) 行输出 (Yes);否则在第 (i) 行输出 (No)。注意输出不含引号。
输入输出样例
输入 #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
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
说明/提示
【输入输出样例 1 说明】
编号为 1 的工人想生产第 1 阶段的零件,需要编号为 2 的工人提供原材料。
编号为 2 的工人想生产第 1 阶段的零件,需要编号为 1 和 3 的工人提供原材料。
编号为 3 的工人想生产第 1 阶段的零件,需要编号为 2 的工人提供原材料。
编号为 1 的工人想生产第 2 阶段的零件,需要编号为 2 的工人生产第 1 阶段的零 件,需要编号为 1 和 3 的工人提供原材料。
编号为 2 的工人想生产第 2 阶段的零件,需要编号为 1 和 3 的工人生产第 1 阶段的零件,他/她们都需要编号为 2 的工人提供原材料。
编号为 3 的工人想生产第 2 阶段的零件,需要编号为 2 的工人生产第 1 阶段的零件,需要编号为 1 和 3 的工人提供原材料。
【输入输出样例 2 说明】
编号为 1 的工人想生产第 1 阶段的零件,需要编号为 2 和 5 的工人提供原材料。
编号为 1 的工人想生产第 2 阶段的零件,需要编号为 2 和 5 的工人生产第 1 阶段的零件,需要编号为 1,3,4 的工人提供原材料。
编号为 1 的工人想生产第 3 阶段的零件,需要编号为 2 和 5 的工人生产第 2 阶段的零件,需要编号为 1,3,4 的工人生产第 1 阶段的零件,需要编号为 2,3,4,5 的工人提供原材料。
编号为 1 的工人想生产第 4 阶段的零件,需要编号为 2 和 5 的工人生产第 3 阶段的零件,需要编号为 1,3,4 的工人生产第 2 阶段的零件,需要编号为 2,3,4,5 的工人生产第 1 阶段的零件,需要全部工人提供原材料。
编号为 1 的工人想生产第 5 阶段的零件,需要编号为 2 和 5 的工人生产第 4 阶段的零件,需要编号为 1,3,4 的工人生产第 3 阶段的零件,需要编号为 2,3,4,5 的工人生产第 2 阶段的零件,需要全部工人生产第 1 阶段的零件,需要全部工人提供原材料。
【数据规模与约定】
共 20 个测试点。
(1 leq u, v, a leq n)。
测试点 1~4,(1 leq n, m leq 1000),(q = 3),(L = 1)。
测试点 5~8,(1 leq n, m leq 1000),(q = 3),(1 leq L leq 10)。
测试点 9~12,(1 leq n, m, L leq 1000),(1 leq q leq 100)。
测试点 13~16,(1 leq n, m, L leq 1000),(1 leq q leq 10^5)。
测试点 17~20,(1 leq n, m, q leq 10^5),(1 leq L leq 10^9)。
思路
(很容易可以想到没有环的情况,只需要判断奇偶性即可。为什么只需要判断奇偶性呢?我们可以想一下,假如说点 A 和点 B 之间有一条边相连,现在需要点 A 生产第7阶段的零件,那么 B 就要生产第6阶段的零件, A 又要生产第5阶段的零件,所以一条边走两遍就只需要判断奇偶性。因为每条边之间没有边权,我们可以假设都是1,这样就可以想到用BFS做。因为只需要判断路径的奇偶性,而且若路径长度过长(比如1号点跟3号点之间的距离是7,但是3号点只需要生产第3阶段的零件,这肯定就不行了)也不行,所以如果这两点之间的最短路过长就不满足条件了,不是最短路只要奇偶性一样,只要走两遍一条边就可以转换回去。所以只需要求出两点之间的奇最短路和偶最短路。由于我们只需要求出1号结点与其它结点的最短路,所以很明显是单源最短路。Dijkstra的原理是贪心,环的情况处理不了(我这个题用不行,可能是我菜QAQ)而SPFA的原理恰巧就是BFS。所以我们就可以用SPFA来求出1号结点与其它结点的奇最短路和偶最短路,再判断一下奇偶性就可以了。别忘记特判两点之间没有奇最短路或偶最短路的情况,这样就可以切掉这道题了。~~我居然用了3天时间才AK了去年的普及组的题目,还看了题解,我是垃圾。~~)
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N=100005,M=200005;
int edge[M],head[N],Next[M],ver[M],d[N];
bool v[N];
int t,n,m,tot;
int ou[N],ji[N];//汉语拼音QAQ
queue<int> q;
void add(int x,int y){
ver[++tot]=y;
edge[tot]=1;
Next[tot]=head[x];
head[x]=tot;
}//存图
void SPFA(){
for(int i=0;i<=N-4;i++){
ou[i]=10000000;
}//为了便于判断有没有奇最短路或偶最短路,我存了10000000
for(int i=0;i<=N-4;i++){
ji[i]=10000000;
}
memset(v,0,sizeof(v));
ou[1]=0;
v[1]=1;
q.push(1);
while(!q.empty()){
int x=q.front();
q.pop();
v[x]=0;
for(int i=head[x];i;i=Next[i]){
int y=ver[i],z=1;
if(ou[x]+z<ji[y]){//因为边权都是1,所以偶数肯定是由奇数+1得来,奇数也同理
ji[y]=ou[x]+z;
if(!v[y]) q.push(y),v[y]=1;
}
if(ji[x]+z<ou[y]){
ou[y]=ji[x]+z;
if(!v[y]) q.push(y),v[y]=1;
}
}
}
}//正常的SPFA
int main()
{
scanf("%d%d%d",&t,&n,&m);
for(int i=1,u,r;i<=n;i++){
scanf("%d%d",&u,&r);
add(u,r);
add(r,u);
}
SPFA();
int a,l;
while(m--){
scanf("%d%d",&a,&l);
if(l%2==0){
if(ou[a]>l){
printf("No
");
}//如果最短路距离过长,那么肯定不行
else{
if(ou[a]==10000000){
printf("No
");//没有偶最短路
}
else{
printf("Yes
");
}
}
}
else{
if(ji[a]>l){
printf("No
");
}
else{
if(ji[a]==10000000){
printf("No
");
}
else{
printf("Yes
");
}
}
}
}
return 0;
}
对Dijkstra和SPFA认识的加深
(一开始这个题我是用Dijkstra来做的,但是处理不了有环的情况(也有可能是我写错了),只得了35pts。但是我只改了这一个地方,就是把Dijkstra改成SPFA,它就神奇的A了,这也不得不引起我对这两个最短路算法的加深思考:到底最大的差别是什么?其实它们之间的代码几乎完全一样,只有两个差别:1.Dijkstra用的是小根堆来存储最短路,而SPFA用的是队列。2.Dijkstra不会重复标记点,即这个点我走过之后不会重复走。但是SPFA可以再将出队的点入队,这也是Dijkstra不能处理负边权而SPFA可以的原因。回归证明的原理,Dijkstra的原理是贪心,而SPFA是广搜。由于边权都是正的,所以Dijkstra在与当前点相连的边中取最小的加入小根堆并更新最短路,那么这个最短路肯定不会再被更新。因为如果它被更新,那肯定是这两点之间存在一个断点,使得两点分别到断点的距离之和小于直接相连。但是由于我选的是最短的那条边,所以起点与断点相连的那条边不可能更短,也就是不可能更新最短路。但反之,如果存在负边权,是存在这种情况的,所以出队的元素可能要再入队一遍来更新最小值。对这个题而言,由于是小根堆,它每次都是找最近的那个点然后走,那肯定不能绕一圈回来更新自己,但是SPFA可以,所以不能用Dijkstra而应该用SPFA。)