链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3315
题解
每个结点的一个
1
1
1看作一个游戏,每次我把这个
1
1
1拿走后,产生的后继局面就是其临点上的若干个
1
1
1
而这新产生的若干个
1
1
1组成一个游戏,每个
1
1
1又是一个子游戏,因此可以用
x
o
r
xor
xor求出这个局面的
S
G
SG
SG
问题就是怎么枚举
C
100
15
C_{100}^{15}
C10015这么多种选择,如果每种情况都看的话,肯定要
T
L
E
TLE
TLE了
但是异或有个性质,异或偶数次相当于没异或,因此我只需要枚举每个点被异或了奇数次还是偶数次就行了,判断一下这个局面是否可行,如果可行就求出异或和之后扔到一个集合
S
S
S里去,最后用
m
e
x
(
S
)
mex(S)
mex(S)算出当前局面的
S
G
SG
SG值
注意,虽然
S
G
SG
SG的值只可能有最多
100
100
100中,但是并不意味着
S
G
SG
SG的值就小于
100
100
100,因为过程中用了很多异或值,粗略考虑的话,
S
G
SG
SG的上界应该设为
2
15
2^{15}
215
代码
//SG定理
#include <bits/stdc++.h>
#define linf (1ll<<60)
#define iinf 0x3f3f3f3f
#define dinf 1e100
#define cl(x) memset(x,0,sizeof(x))
#define maxn 2019
using namespace std;
typedef long long ll;
ll SG[maxn], k[maxn], cd[maxn], lis[maxn], rd[maxn], head[maxn], to[maxn], nex[maxn], etot, v[maxn], V, E, cnt[32768],
tmp[32799];
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
void adde(ll a, ll b){to[++etot]=b;nex[etot]=head[a];head[a]=etot;}
void init()
{
ll i, a, b;
V=read(), E=read();
for(i=1;i<=E;i++)
{
a=read(), b=read();
adde(a,b);
cd[a]++;
rd[b]++;
}
for(i=0;i<V;i++)k[i]=read();
for(i=0;i<32768;i++)cnt[i]=cnt[i>>1]+(i&1);
}
void BFS()
{
ll x, p, i;
queue<ll> q;
for(i=0;i<V;i++)if(rd[i]==0)q.push(i);
while(!q.empty())
{
x=q.front(), q.pop();
lis[++*lis]=x;
for(p=head[x];p;p=nex[p])
{
rd[to[p]]--;
if(rd[to[p]]==0)q.push(to[p]);
}
}
}
ll mex()
{
ll i, x=1;
sort(tmp+1,tmp+*tmp+1);
for(i=2;i<=*tmp;i++)if(tmp[i]!=tmp[i-1])tmp[++x]=tmp[i];
tmp[x+1]=-1;
for(i=1;;i++)if(tmp[i]!=i-1)return i-1;
}
void dp()
{
ll i, p, b, x, t, nim;
for(i=V;i;i--)
{
x=lis[i];
if(cd[x]==0)continue;
cl(tmp);
for(b=0;b<(1ll<<cd[x]);b++)
if((cnt[b]&1)==(k[x]&1) and cnt[b]<=k[x])
{
for(t=b,p=head[x],nim=0;p;p=nex[p],t>>=1)
if(t&1)nim^=SG[to[p]];
tmp[++*tmp]=nim;
}
SG[x]=mex();
}
}
void play()
{
ll i, ans=0;
for(i=0;i<V;i++)if(read()&1)ans^=SG[i];
if(ans==0)printf("LOSING\n");
else printf("WINNING\n");
}
int main()
{
ll T=read(), R, i, kase=0;
while(T--)
{
cl(head), cl(nex), cl(SG), cl(cd), cl(rd), cl(lis), cl(tmp), etot=0;
printf("Game#%lld:\n",++kase);
init();
BFS();
dp();
R=read();
for(i=1;i<=R;i++)
{
printf("Round#%lld: ",i);
play();
}
putchar(10);
}
return 0;
}