题目大意
给定一张n个点
两个人轮流在图上进行操作。每次操作时,选择一个有出边(“下棋”一题没有这个限制)且点上有石子的点
请问先手是否存在必胜策略。
n≤200,m≤5000,q≤105,ci≤5000
题目分析
可以发现每个石子其实看成一个单独的游戏。
考虑如何计算一个石子的Sg函数值。
Sg(x)=mexs⊆S{xor(x,y)∈sSg(y)}
这个怎么算呢?我们把同颜色的边先异或起来,然后对这些数求线性基,假设i是最小的线性相关的位(线性基该位为
然后就可以用bitset瞎压一下位来做了。
注意,如果必须选择有出边的点的话,一个没有出边的点Sg值应该是0,如果没有这个限制就是
代码实现
#include <iostream>
#include <cstdio>
#include <bitset>
#include <cctype>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=205;
const int M=5005;
const int C=5005;
typedef bitset<N> B;
B base[N],sg[N],cbit[C],ans;
int nxt[M],tov[M],col[M];
bool vis[N];
int last[N];
int used[C];
int ext[C];
int n,m,tot,q;
void insert(int x,int y,int z){tov[++tot]=y,nxt[tot]=last[x],col[tot]=z,last[x]=tot;}
void dfs(int x)
{
if (vis[x]) return;
vis[x]=1;
if (!last[x]) return;
for (int i=last[x];i;i=nxt[i]) dfs(tov[i]);
ext[0]=0;
for (int i=last[x];i;i=nxt[i])
{
if (used[col[i]]!=x) cbit[col[i]].reset(),used[ext[++ext[0]]=col[i]]=x;
cbit[col[i]]^=sg[tov[i]];
}
for (int i=0;i<=n;i++) base[i].reset();
for (int i=1;i<=ext[0];++i)
{
for (int j=n;j>=0;--j)
if (cbit[ext[i]].test(j))
if (base[j].test(j)) cbit[ext[i]]^=base[j];
else
{
base[j]=cbit[ext[i]];
break;
}
}
for (int i=0;i<=n;++i)
if (base[i].none())
{
sg[x].set(i);
break;
}
}
int main()
{
freopen("game.in","r",stdin),freopen("game.out","w",stdout);
n=read(),m=read();
for (int i=1,x,y,z;i<=m;++i) x=read(),y=read(),z=read(),insert(x,y,z);
for (int i=1;i<=n;++i) dfs(i);
for (q=read();q--;) ans^=sg[read()];
printf("%d\n",ans.any());
fclose(stdin),fclose(stdout);
return 0;
}