SDOI2009

E&D

时间限制 1.00s 内存限制 125.00MB

题目描述

小 E 与小 W 进行一项名为 E&D 游戏。

游戏的规则如下:桌子上有 2n 堆石子,编号为 12n 。其中,为了方便起见,我们将第 2k1 堆与第 2k1kn视为同一组。第 i 堆的石子个数用一个正整数 Si 表示。

一次分割操作指的是,从桌子上任取一堆石子,将其移走。然后分割它同一组的另一堆石子,从中取出若干个石子放在被移走的位置,组成新的一堆。操作完成后,所有堆的石子数必须保证大于 1 。显然,被分割的一堆的石子数至少要为 2 。两个人轮流进行分割操作。如果轮到某人进行操作时,所有堆的石子数均为 1 ,则此时没有石子可以操作,判此人输掉比赛。

小 E 进行第一次分割。他想知道,是否存在某种策略使得他一定能战胜小 W。因此,他求助于小 F,也就是你,请你告诉他是否存在必胜策略。例如,假设初始时桌子上有 4 堆石子,数量分别为 1,2,3,1 。小 E 可以选择移走第 1 堆,然后将第 2 堆分割(只能分出 1 个石子)。接下来,小 W 只能选择移走第 4 堆,然后将第 3 堆分割为 12 。最后轮到小 E,他只能移走后两堆中数量为 1 的一堆,将另一堆分割为 11 。这样,轮到小 W 时,所有堆的数量均为 1 ,则他输掉了比赛。故小 E 存在必胜策略。

输入格式

本题有多组数据。

第一行一个整数 T,表示数据组数。

对于每组数据:

第一行一个整数 N,表示桌子上共有 N 堆石子,这里的 N 即为题目描述中的 2n

第二行 N 个整数 S1N

输出格式

对于每组数据,如果小 E 必胜,则一行一个字符串 YES,否则一行一个字符串 NO

输入输出样例

输入

2 4 1 2 3 1 6 1 1 1 1 1 1

输出

YES NO

说明/提示

对于 20% 的数据,N=2

对于另外 20% 的数据,N4Si50

对于 100% 的数据,1T201N2×104N 为偶数,1Si2×109

原题链接

P2148 [SDOI2009]E&D

题目大意

给定n对石子堆,每次操作针对一对石子堆:去掉一堆,将另外一堆石子进行分割,保证所有石子堆数量不为0

解题思路

博弈论,sg函数

  • sg定理
    [公式] 为ICG的状态,则: [公式]

nim游戏,N堆石子,单位是一对堆石子,可以先把所有单位的sg函数求出,然后再根据sg定理进而求出整堆石子的sg函数。
首先,sg(1,1)为必败点,即sg(1,1)=0,设i=j+k,则sgi,)=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; }

打表结果:
image
根据打表结果,

  1. ij,有sg(i,j)=0
  2. sg(i,j)=sg(j,i)
  3. n 列和 2n 列有一定联系,对 j,sg(i,j)=sg(i12+1,j2)+1
    可以递归求解
  • 算法复杂度:o(nlogn)

代码

#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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/15178409.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示