Luogu P5663 CSP-J2019 加工零件 題解 [ 綠 ] [ 同餘最短路 ]

KS_Fszha發表於2024-09-29

加工零件:非常好的一道圖論題。CCF 普及組的題目大概也只有圖論出的比較巧妙了。

題意簡述:給你一張無向圖,\(q\) 次詢問,判斷是否存在一條從 \(a\)\(1\) 且長度為 \(L\) 的路徑。

看到 \(L\) 很大,我們立刻想到了要撇開 \(L\) 的限制思考問題。

首先,對於一條路徑,我們肯定能找到從 \(1\)\(v\) 的一條最短路徑,它的長度為 \(s\)

此時我們可以發現,這時候我們一定可以找到長度為 \(s,s+2,s+4,...,s+2k\) 的路徑。

為什麼?因為這張圖是無向圖,我們可以沿著一條邊走,來回一趟,這樣我們的時間就會增加 \(2\) 了。

那麼假設我們要 \(s+1,s+3,...,s+2k+1\) 的長度怎麼辦?我們只需要找到一個長度為 \(s+2k+1\) 的最短路徑即可,這樣長度為 \(s+2k+1+2j\) 的路徑就都能找到。

這就啟發我們把一個點分為此時時間為奇數和此時時間為偶數兩種了。

我們可以把點翻倍,然後 BFS 的過程中記錄此時是奇數路徑還是偶數路徑,進入到相應狀態的點中,每個點的每個狀態最多隻會走到一次,這樣就能在 \(O(n)\) 內求解了。這便是同於最短路的一個簡單應用。

那麼每次詢問怎麼處理?顯然,當 \(L\) 為奇數時,判斷它是否大於等於奇數狀態的這個點的最短路長度。若是,則說明可以,否則說明到不了。因為他們是同餘的。偶數同理。

寫程式碼的時候把 bitset 兩維弄反了,喜提 RE,竟然還把樣例過掉了,離譜 CCF。

注意 bitset 尖括號裡的那一維是最後一維!!!進食厚仁!!!

程式碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
int n,m,t,dis[1000005][2];
vector<int>g[1000005];
struct node{
	int u,s,d;
};
queue<node>q;
bitset<2>vis[1000005];
void init()
{
	memset(dis,0x3f,sizeof(dis));
	dis[1][0]=0;
	vis[1][0]=1;
	q.push({1,0,0});
	while(!q.empty())
	{
		node tmp=q.front();
		int u=tmp.u,s=tmp.s,d=tmp.d,ns=(s+1)%2;
		q.pop();
		for(auto v:g[u])
		{
			if(vis[v][ns]==0)
			{
				vis[v][ns]=1;
				dis[v][ns]=d+1;
				q.push({v,ns,d+1});
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>t;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	init();
	while(t--)
	{
		int a,l;
		cin>>a>>l;
		if(l%2==1)
		{
			if(dis[a][1]<=l)cout<<"Yes"<<endl;
			else cout<<"No"<<endl;
		}
		else
		{
			if(dis[a][0]<=l)cout<<"Yes"<<endl;
			else cout<<"No"<<endl;			
		}
	}
	return 0;
}

相關文章