题解 洛谷P6560 [SBCOI2020] 时光的流逝

题目大意

题目链接

给定一个(n)个点,(m)条边的有向图(不保证无环)。(q)次询问。每次指定一组起点和终点,并在起点处放一枚棋子。

有两个游戏玩家,轮流移动棋子(只能顺着图上的边移动,每人每次只能移动一步)。

先将棋子移动到终点的人立即获胜。如果谁无法继续移动了,那么他失败,对手获胜。

问先、后手是否有必胜策略。如果先手有必胜策略输出(1),如果后手有必胜策略输出(-1),如果两人都没有必胜策略输出(0)

数据范围:(1leq nleq 10^5)(1leq mleq 5 imes 10^5)(1leq qleq 500)。保证起点和终点不同。

本题题解

考虑,给出的图如果是一个DAG(无环),我们可以对反图做拓扑排序,同时推出答案。在反图上,所有入度为(0)的点,都是先手必败;询问给定的终点也是先手必败(因为距离它为(1)的点肯定是先手必胜了,所以可以把它理解为先手必败)。然后对于其他点,如果存在至少一个能到达它的点,是先手必败的,那么它先手必胜;否则它是先手必败。这样递推一下就能求出答案了,时间复杂度是(O(qm))的。

再考虑有环的情况。有环和无环最大的区别是,正常的拓扑排序时,我们无法进入到环里。那么一个环,就可能“隔绝”一些已知的答案。具体来说,对于某个节点:

  • 如果(在反图上)至少存在一个能到达它的点是先手必败的,那该节点一定是先手必胜的。无论它在不在环上,入度是否为(0),我们都把它加入到队列里,并且之后不再访问它(一个点不能入队两次)。
  • 如果没有发现,至少一个,能到达它的、先手必败的点。那么我们看当前节点入度是否已经清零。如果已经清零,说明它不在环上,而且我们已经考虑过了所有能到达它的边,因此可以直接断定它是先手必败的,然后加入队列即可。
  • 如果它的入度还没有清零,说明它一定在某个环上。这种情况下先、后手都可以在环上无限地绕圈。所以此时该节点的状态是未知的。

整个过程,和普通的拓扑排序还是很类似的。主要的区别是,一旦确定了一个节点的状态(必胜或必败),就立即加入到队列中,并且从此不再访问它。这样可以避免“环”对传递答案造成的不必要的“隔绝”。根据这个原则,我们初始时也会把终点加入到队列中,无论它入度是否为(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;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/13329521.html