UVA12163 - Addition-Subtraction Game

链接

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值