题目地址:
http://acm.tju.edu.cn/toj/showp4172.html
题目概述:
有一个长度为n的0/1序列,每次操作可以将两个连续的1变成0,问先手是否必胜。
大致思路:
博弈论还是很容易看出来的,不过比赛的时候忘了sg函数的定义导致这题一直WA,要吸取教训啊,对于这些学过的东西要经常复习复习。
sg函数的定义是:sg(x)=mex{sg(y) | y是x的后继 }。mex表示最小的不属于这个集合的非负整数。
辣么我们设sg(i)为一段全为1且长度是i的序列的sg值,则i的后继应该是sg(j) xor sg(i-j-2) ,j从0到i-2
数据比较小,预处理一下所有长度的sg值就好了。
复杂度分析:
sg值的计算是O(n2)的,每次询问是O(n),总结起来就是O(n2)。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <vector> #include <ctime> #include <map> #include <assert.h> #include <stack> #include <set> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define sacnf scanf #define scnaf scanf #define maxn 1010 #define maxm 20010 #define inf 1061109567 #define INF 0x3f3f3f3f #define Eps 0.000001 const double PI=acos(-1.0); #define mod 1000000007 #define MAXNUM 10000 #define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++) #define mes(a,b) memset((a),(b),sizeof(a)) typedef long long ll; typedef unsigned long long ulld; void Swap(int &a,int &b) {int t=a;a=b;b=t;} ll Abs(ll x) {return (x<0)?-x:x;} int sg[maxn],num[maxn]; int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); //clock_t st=clock(); int n=1000; sg[0]=sg[1]=0; for(int i=2;i<=n;i++) { for(int j=0;j<=i;j++) num[j]=0; for(int j=0;j+2<=i;j++) num[sg[j]^sg[i-2-j]]=1; for(int j=0;;j++) if(!num[j]) {sg[i]=j;break;} } //for(int i=0;i<=n;i++) if(!sg[i]) cout<<i<<" "; int T;scanf("%d",&T); while(T--) { int n,x;scanf("%d",&n); int len=0,ans=0; for(int i=1;i<=n;i++) { scanf("%1d",&x); if(x==1) len++; else { if(len!=0) ans^=sg[len];len=0; } } if(len!=0) ans^=sg[len]; printf(ans!=0?"Alice\n":"Bob\n"); } //clock_t ed=clock(); //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC); return 0; }
时间: 2024-10-11 01:53:00