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

题目传送门

样例真的水爆了

进入正题

题目中要求每次从一个点向相连的点扩散,这是不是和洪水填充很像?于是想到最短路

可是乍一看好像和最短路没什么关系啊,于是我们来看样例。

样例虽然水,但有一点值得关注:奇偶性。

观察样例,当从 \(1\) 出发时,如果 \(L\) 为奇数,那么就是 \(No\) 否则就是 \(Yes\) (这里暂时不考虑环的情况)。

由于存在“向前->返回->向前->返回”这样的情况,因此我们可以一次性走到点 \(1\),然后在点 \(1\) 和与它相连的点来回走,如果最终停在点 \(1\) 上则小伟需要提供原材料,否则不需要。

注意到来回走一轮后,“距离”的奇偶性不会改变,因此我们可以计算出走到某一点需要奇数步的最短路和偶数步的最短路,然后与 \(L\) 进行比较。

细节见代码:(我用了 Dijkstra,其实 BFS 也行)

#include<bits/stdc++.h>
#define rd(n) scanf("%d",&n);
using namespace std;
const int N=1e5+10;
int n,m,q,dis[N][2],sta;
vector<int> f[N];
bool vis[N][2];//第一维是节点编号,第二维是奇偶性 
struct P {
	int d,v,p;
	//d:相连的节点的编号;v:距离;p:奇偶性 
	bool operator <(const P&a)const {
		return v>a.v;
	}
};
void Dij() {
	memset(dis,0x3f,sizeof(dis));
	priority_queue<P> q;//堆优化 
	sta=dis[0][0];//走不到的标准 
	q.push({1,0,0});
	dis[1][0]=0;
	while(!q.empty()) {
		P t=q.top();
		q.pop();
		if(vis[t.d][t.p])continue;
		vis[t.d][t.p]=1;
		for(int j=0;j<f[t.d].size();j++){
			int x=f[t.d][j];
			if(dis[x][t.p^1]>dis[t.d][t.p]+1){
				//松弛(走一步后奇偶性会改变,因此 t.p^1) 
				dis[x][t.p^1]=dis[t.d][t.p]+1;
				q.push({x,dis[x][t.p^1],t.p^1});
			}
		}
	}
}
int main() {
	rd(n)rd(m)rd(q)
	for(int i=1,u,v; i<=m; i++) {
		rd(u)rd(v)
		f[u].push_back(v);//建图不解释(双向边) 
		f[v].push_back(u);
	}
	Dij();//Dijkstra跑一轮 
	for(int i=1,a,l;i<=q;i++){
		rd(a)rd(l)
		if(dis[a][1]<=l and dis[a][1]!=sta and l%2==1){
			//当奇数步能走到点1,并且l也为奇数时,最终恰好停在点1 
			printf("Yes\n");
		}
		else if(dis[a][0]<=l and dis[a][0]!=sta and l%2==0){
			//偶数步同理 
			printf("Yes\n");
		}
		else printf("No\n"); 
	}
	return 0;
}

AC

完结撒花!

posted @ 2021-06-23 13:44  Maplisky  阅读(516)  评论(0编辑  收藏  举报