題解 洛谷P6560 [SBCOI2020] 時光的流逝
阿新 • • 發佈:2020-07-17
題目大意
給定一個\(n\)個點,\(m\)條邊的有向圖(不保證無環)。\(q\)次詢問。每次指定一組起點和終點,並在起點處放一枚棋子。
有兩個遊戲玩家,輪流移動棋子(只能順著圖上的邊移動,每人每次只能移動一步)。
先將棋子移動到終點的人立即獲勝。如果誰無法繼續移動了,那麼他失敗,對手獲勝。
問先、後手是否有必勝策略。如果先手有必勝策略輸出\(1\),如果後手有必勝策略輸出\(-1\),如果兩人都沒有必勝策略輸出\(0\)。
資料範圍:\(1\leq n\leq 10^5\),\(1\leq m\leq 5\times 10^5\),\(1\leq q\leq 500\)。保證起點和終點不同。
本題題解
考慮,給出的圖如果是一個DAG(無環),我們可以對反圖做拓撲排序,同時推出答案。在反圖上,所有入度為\(0\)的點,都是先手必敗;詢問給定的終點也是先手必敗(因為距離它為\(1\)的點肯定是先手必勝了,所以可以把它理解為先手必敗)。然後對於其他點,如果存在至少一個能到達它的點,是先手必敗的,那麼它先手必勝;否則它是先手必敗。這樣遞推一下就能求出答案了,時間複雜度是\(O(qm)\)的。
再考慮有環的情況。有環和無環最大的區別是,正常的拓撲排序時,我們無法進入到環裡。那麼一個環,就可能“隔絕”一些已知的答案。具體來說,對於某個節點:
- 如果(在反圖上)至少存在一個能到達它的點是先手必敗的,那該節點一定是先手必勝
- 如果沒有發現,至少一個,能到達它的、先手必敗的點。那麼我們看當前節點入度是否已經清零。如果已經清零,說明它不在環上,而且我們已經考慮過了所有能到達它的邊,因此可以直接斷定它是先手必敗的,然後加入佇列即可。
- 如果它的入度還沒有清零,說明它一定在某個環上。這種情況下先、後手都可以在環上無限地繞圈。所以此時該節點的狀態是未知的。
整個過程,和普通的拓撲排序還是很類似的。主要的區別是,一旦確定了一個節點的狀態(必勝或必敗),就立即加入到佇列中,並且從此不再訪問它。這樣可以避免“環”對傳遞答案造成的不必要的“隔絕”。根據這個原則,我們初始時也會把終點加入到佇列中,無論它入度是否為\(0\)
時間複雜度\(O(qm)\)。
參考程式碼:
//problem:P6560
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
const int MAXN=1e5,MAXM=5e5;
struct EDGE{int nxt,to;}edge[MAXM+5];
int head[MAXN+5],tot;
inline void add_edge(int u,int v){edge[++tot].nxt=head[u],edge[tot].to=v,head[u]=tot;}
int n,m,q,in_degree[MAXN+5],cur_deg[MAXN+5],st,ed,f[MAXN+5];
int main() {
cin>>n>>m>>q;
for(int i=1;i<=m;++i){
int u,v;
cin>>u>>v;
add_edge(v,u);//反向邊
in_degree[u]++;
}
for(int tq=1;tq<=q;++tq){
cin>>st>>ed;
queue<int>que;
for(int i=1;i<=n;++i){
cur_deg[i]=in_degree[i];
if(!cur_deg[i] || i==ed)
f[i]=-1,que.push(i);
else
f[i]=0;
}
while(!que.empty()){
int u=que.front(); que.pop();
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(f[v]!=0)continue;
cur_deg[v]--;
if(f[u]==-1){
f[v]=1;
que.push(v);
}
else if(!cur_deg[v]){
if(f[v]!=1)
f[v]=-1;
que.push(v);
}
}
}
cout<<f[st]<<endl;
}
return 0;
}