把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷4836】Toy Train 玩具火车(博弈)

题目链接

  • 有两个人(标号为 \(0/1\))和一张 \(n\) 个点 \(m\) 条边的有向图,保证每个点至少有一条出边。
  • \(i\) 个点颜色为 \(a_i\)\(0/1\)),且它被标号为 \(p_i\) 的人占据。
  • 每个人需要为他占据的所有点分别只保留一条出边。
  • 对于每个点,标号为 \(0\) 的人希望它能进入一个 \(a_i\) 全为 \(0\) 的环,标号为 \(1\) 的人希望它不能进入这样一个环,求标号为 \(1\) 的人能否获胜。
  • \(1\le n\le5\times10^3\)\(n\le m\le2\times10^4\)

类似博弈的思路

首先考虑找出哪些点不能构成全 \(0\) 的环。

显然,\(a_i=1\) 的点不能构成全 \(0\) 的环。

然后,若一个点被 \(1\) 占据且存在一个邻点无法构成全 \(0\) 的环,则 \(1\) 可以选择它走向这个点,无法构成;若一个点被 \(0\) 占据且所有邻点都无法构成全 \(0\) 的环,也无法构成。

这样做一个有点像拓扑的过程,对于剩下来的点,随便指定一条边都将构成若干全 \(0\) 的基环内向树,也就是说它们都必然满足能进入全 \(0\) 的环。

再进行一个类似于前面的反过程,若一个点被 \(0\) 占据且存在一个邻点能进入全 \(0\) 的环,则 \(0\) 可以选择它走向这个点,能进入;若一个点被 \(1\) 占据且所有邻点都能进入全 \(0\) 的环,也能进入。

但发现这样有一个问题,就是在第二个过程中,有可能虽然 \(1\) 可以选择不走到能进入全 \(0\) 的环的邻点,但这样选择会导致可以构成新的全 \(0\) 环(一个非常简单的例子,就是这个点颜色为 \(0\) 且除了能进入全 \(0\) 的环的邻点之外只剩一个自环,那么此时只能选自环,而选自环就构成了一个全 \(0\) 的环)。

要处理这种情况其实也很容易,只要删去所有能进入全 \(0\) 的环的点,不断重复上述两个过程,直至不再产生新的能进入全 \(0\) 的环的点即可。

代码:\(O(nm)\)

#include<bits/stdc++.h>
#define Cn const
#define CI Cn int&
#define N 5000
#define M 20000
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define Tp template<typename Ty>
	#define Ts template<typename Ty,typename... Ar>
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp void read(Ty& x) {x=0;while(!isdigit(oc=tc()));while(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int n,m,o[N+5],p[N+5],d[N+5],D[N+5],q[N+5],g[N+5],vis[N+5];vector<int> G[N+5],iG[N+5];
int main()
{
	int i;for(read(n,m),i=1;i<=n;++i) read(o[i]);for(i=1;i<=n;++i) read(p[i]);
	int x,y;for(i=1;i<=m;++i) read(x,y),++D[++x],++y,G[x].push_back(y),iG[y].push_back(x);
	int k,H,T;while(true)
	{
		for(H=1,T=0,i=1;i<=n;++i) !g[i]&&(p[i]?vis[q[++T]=i]=1:vis[i]=0),d[i]=D[i];
		while(H<=T) {k=q[H++];for(auto x:iG[k]) !g[x]&&!vis[x]&&(o[x]||!--d[x])&&(vis[q[++T]=x]=1);}//找出不能构成全0环的点
		for(H=1,T=0,i=1;i<=n;++i) !g[i]&&!vis[i]&&(g[q[++T]=i]=1);if(!T) break;//剩下的点能进入全0环
		while(H<=T) {k=q[H++];for(auto x:iG[k]) !g[x]&&(!o[x]||!--D[x])&&(g[q[++T]=x]=1);}//更新能进入全0环的点
	}
	for(i=1;i<=n;++i) puts(g[i]?"No":"Yes");return 0;
}
posted @ 2022-07-01 13:28  TheLostWeak  阅读(86)  评论(0编辑  收藏  举报