題解 P5663 [CSP-J2019] 加工零件
阿新 • • 發佈:2021-06-23
樣例真的水爆了。
進入正題
題目中要求每次從一個點向相連的點擴散,這是不是和洪水填充很像?於是想到最短路。
可是乍一看好像和最短路沒什麼關係啊,於是我們來看樣例。
樣例雖然水,但有一點值得關注:奇偶性。
觀察樣例,當從 \(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; }