时间限制 1.00s 内存限制 125.00MB
小 E 与小 W 进行一项名为 E&D
游戏。
游戏的规则如下:桌子上有 \(2n\) 堆石子,编号为 \(1 \sim 2n\) 。其中,为了方便起见,我们将第 \(2k-1\) 堆与第 \(2k\) 堆\((1 \le k \le n)\)视为同一组。第 \(i\) 堆的石子个数用一个正整数 \(S_i\) 表示。
一次分割操作指的是,从桌子上任取一堆石子,将其移走。然后分割它同一组的另一堆石子,从中取出若干个石子放在被移走的位置,组成新的一堆。操作完成后,所有堆的石子数必须保证大于 \(1\) 。显然,被分割的一堆的石子数至少要为 \(2\) 。两个人轮流进行分割操作。如果轮到某人进行操作时,所有堆的石子数均为 \(1\) ,则此时没有石子可以操作,判此人输掉比赛。
小 E 进行第一次分割。他想知道,是否存在某种策略使得他一定能战胜小 W。因此,他求助于小 F,也就是你,请你告诉他是否存在必胜策略。例如,假设初始时桌子上有 \(4\) 堆石子,数量分别为 \(1,2,3,1\) 。小 E 可以选择移走第 \(1\) 堆,然后将第 \(2\) 堆分割(只能分出 \(1\) 个石子)。接下来,小 W 只能选择移走第 \(4\) 堆,然后将第 \(3\) 堆分割为 \(1\) 和 \(2\) 。最后轮到小 E,他只能移走后两堆中数量为 \(1\) 的一堆,将另一堆分割为 \(1\) 和 \(1\) 。这样,轮到小 W 时,所有堆的数量均为 \(1\) ,则他输掉了比赛。故小 E 存在必胜策略。
本题有多组数据。
第一行一个整数 \(T\),表示数据组数。
对于每组数据:
第一行一个整数 \(N\),表示桌子上共有 \(N\) 堆石子,这里的 \(N\) 即为题目描述中的 \(2n\) 。
第二行 \(N\) 个整数 \(S_{1 \dots N}\) 。
对于每组数据,如果小 E 必胜,则一行一个字符串 YES
,否则一行一个字符串 NO
。
输入
2 4 1 2 3 1 6 1 1 1 1 1 1
输出
YES NO
对于 \(20\%\) 的数据,\(N=2\) 。
对于另外 \(20\%\) 的数据,\(N \le 4,S_i \le 50\) 。
对于 \(100\%\) 的数据,\(1 \le T \le 20,1 \le N \le 2 \times 10^4\) 且 \(N\) 为偶数,\(1 \le S_i \le 2 \times 10^9\) 。
给定n对石子堆,每次操作针对一对石子堆:去掉一堆,将另外一堆石子进行分割,保证所有石子堆数量不为0
博弈论,sg函数
nim游戏,N堆石子,单位是一对堆石子,可以先把所有单位的sg函数求出,然后再根据sg定理进而求出整堆石子的sg函数。
首先,\(sg(1,1)\)为必败点,即\(sg(1,1)=0\),设\(i=j+k\),则\(sg(i,……)=mex(sg(j,k))\)。
算起来比较,我们可以用bitset来简化操作,bitset每一位我们用来表示是否出现在mex集合里,如bitset表示的\(s(i)\),意思是所有\(j+k=i\)的sg函数,位数为\(1\)表示该位出现在mex集合里面。
复杂度过大,不过可以用来打表找规律~
打表代码:
#include<bits/stdc++.h> using namespace std; const int N=210; int sg[N][N]; //设i=j+k,由题意,sg[i,…]=mex(sg[k]),s[i]相当于sg函数的集合,不过每个数都用01表示,存储mex元素 bitset<101>s[N]; inline int mex(bitset<101> b) { int i=0; while(b[i])i++; return i; } int main() { for(int i=2;i<=101;i++) for(int j=1,k=i-1;k;j++,k--) s[i].set(sg[j][k]=mex(s[j]|s[k])); for(int i=1;i<=100;i++) { for(int j=1;j<=100;j++) cout<<sg[i][j]<<' '; puts(""); } return 0; }
打表结果:
iujie
根据打表结果,
#include<bits/stdc++.h> using namespace std; int t,n,x,y; inline int sg(int n,int m) { if((n&1)&&(m&1))return 0; else if(!(m&1))return sg((n-1>>1)+1,m>>1)+1; else return sg(m,n); } int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); int res=0; for(int i=0;i<n/2;i++) { scanf("%d%d",&x,&y); res^=sg(x,y); } puts(res?"YES":"NO"); } return 0; }