发现大多数的题解都是不同于官方题解的做法,这里我将介绍官方题解做法。

Solution

证明先手是否可以必胜的方法相差无几,为了方便后边行文,这里介绍我的思路:考虑各堆石子和为奇数的情况(以下简称为“奇状态”,另一种称为“偶状态”)一定先手必胜:两人一次取一个即可。考虑偶状态。可以发现,先手一定要尽可能的一次拿走偶数个,这样就能留给对方一个偶状态使得对方不能必胜。考虑分裂原有状态,将每一堆中的石子成对取出,一定出现一个剩下的偶状态(每堆最多只有一个),另一个是取出来的状态,每对压成一个石子。由于前者每次只能取一个,共偶数个,所以是必败状态。根据“可以发现”的内容,每个选手一定会先成对的取,也就是说取出新状态中压出来的若干石子。所以先手一定会先面对新的状态,如果它是奇状态则先手必胜,后手会先接手前面剩下的状态使其必败。如果当前状态是偶状态,我们考虑重复上述分裂操作。当每一个压出来的石子代表代表的大小大于 \(k\) 时,由于根据“可以发现”内容,先手必定至少会取压出来的一个石子(大于 \(k\) 个),所以先手必败。

以上判断方法直接模拟复杂度为 \(O(n\log(k))\) 级别的。考虑一个奇状态的特征是各堆的石子数量的二进制末位加和在模 \(2\) 意义下为 \(1\),一次分裂后的新状态等同于将各堆的石子数量在二进制下右移一位,也就是说将“末尾”变成“倒数第二位”,显然第一次出现奇状态时如果分裂 \(i\) 次,每个石子实际大小就是 \(2^i\),所有数的从小到大第 \(i+1\) 位加和在模 \(2\) 意义下为 \(1\),且所有第 \(j(j<i)\) 位加和模 \(2\) 意义下均为零。根据异或运算的性质,发现这个最小的 \(i\) 就是初始各堆石子数量异或和最低的 \(1\) 的位置减 \(1\),相对应的,此时每个石子大小为 \(2^i=\operatorname{lowbit}(\oplus_{i}^{i\in[1,n]}a_i)\)。这里我们定义 \(\operatorname{lowbit}(0)=\infty\)。

接下来是求可行解的部分。当先手必胜时,可以发现以下性质:

  1. 先手取的一定是上文提到最后一次分裂每个石子最终大小(以下称 \(s\))的奇数倍。因为上来一定是个奇状态,只有拿奇数个才不会留给对方奇状态。
  2. 先手取完第一次后,每堆石子实际数量的异或和的 \(\operatorname{lowbit}\) 一定变大,因为我们留下的状态是偶状态,由于拿了 \(s\) 的倍数个,更低的位显然不动,由于是偶状态所以不能单出来一个 \(s\),所以 \(s\) 那一位必须是 \(0\)。综上,新的异或和的最低为 \(1\) 的位必定比 \(s\) 那一位大。
  3. 所取的数都在留给后手的那一位 \(\operatorname{lowbit}\) 之前。如果大于等于 \(\operatorname{lowbit}\) 的位改变,设当前 \(\operatorname{lowbit}\) 位代表的值为 \(p\),更大的改变的位代表的值为 \(q\),则\(p\leq{q}\)。此时后手已经得到了一个必胜状态:直接拿走 \(p\)。毕竟先手至少要拿 \(q\)。
  4. 随着钦定剩下的 \(\operatorname{lowbit}\) 增大,要减的数单调递增。发现每次原来 \(\operatorname{lowbit}\) 那一位(以下称 \(i\))之前的位是否要加已经确定(具体可见下文公式,被异或的 \(2^i\) 的后几位永远是 \(0\)),由于上一次的 \(i\) 位一定不取(否则后手只需取 \(i\) 代表的数,就能必胜),只需考虑加不加入这一位即可。

考虑对于每一堆,根据性质 \(2\) 钦定剩下的 \(\operatorname{lowbit}\) 为 \(i\),根据性质 \(3\),设 \(l=\oplus_{i}^{i\in[1,i)\lor(i,n]}a_i\),根据异或的自反性,最终结果一定是 \((a-l\oplus{2^i})(\bmod 2^{i+1})\)(大意是取出异或和中的 \(a\) 求加入何值得到 \(2^i\),由于我们只关心 \(i\) 及以后的位所以取模)。由性质 \(4\) 发现钦定的 \(i\) 递增会使答案递增,无需排序直接输出。

实现时应注意在最后判断 \(\operatorname{lowbit}=\infty\),即将 \(a_i\) 变成 \(l\) 是否合法。

Code

代码
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
long long n,k,a[50100],all[64],xsum;
int main(){
freopen("nim.in","r",stdin);
freopen("nim.out","w",stdout);
all[0]=1;
for(int i=1;i<40;++i) all[i]=all[i-1]<<1|1;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]),xsum^=a[i];
if((!xsum)||lowbit(xsum)>k) return printf("0\n"),0;
printf("1\n");
long long dlim=__lg(lowbit(xsum));
for(int i=1;i<=n;++i){
long long tem=xsum^a[i];
for(long long j=dlim+1;j<40;++j){
long long tt=(a[i]-(tem^(1ll<<j)))&all[j];
if(((tt>>j)&1)) continue;
if(tt>a[i]||tt>k) break;
printf("%d %lld\n",i,tt);
}
if(a[i]-tem<=k&&a[i]>=tem) printf("%d %lld\n",i,a[i]-tem);
}
return 0;
}

HZOI NOIP 2024 Round 24 T2 取石子 官方做法的更多相关文章

  1. [ACM_数学] Fibonacci Nim(另类取石子,2-4组合游戏)

    游戏规则: 有一堆个数为n的石子,游戏双方轮流取石子,满足: 1)先手不能在第一次把所有的石子取完: 2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍). ...

  2. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  3. OpenJudge计算概论-取石子游戏

    OpenJudge计算概论-取石子游戏[函数递归练习] /*====================================================================== ...

  4. T1218:取石子游戏

    [题目描述] 有两堆石子,两个人轮流去取.每次取的时候,只能从较多的那堆石子里取,并且取的数目必须是较少的那堆石子数目的整数倍,最后谁能够把一堆石子取空谁就算赢. 比如初始的时候两堆石子的数目是25和 ...

  5. 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组

    2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...

  6. {HDU}{2516}{取石子游戏}{斐波那契博弈}

    题意:给定一堆石子,每个人最多取前一个人取石子数的2被,最少取一个,最后取石子的为赢家,求赢家. 思路:斐波那契博弈,这个题的证明过程太精彩了! 一个重要的定理:任何正整数都可以表示为若干个不连续的斐 ...

  7. 【BZOJ-3895】取石子 记忆化搜索 + 博弈

    3895: 取石子 Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 263  Solved: 127[Submit][Status][Discuss] D ...

  8. Games:取石子游戏(POJ 1067)

    取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37662   Accepted: 12594 Descripti ...

  9. ACM 取石子(七)

    取石子(七) 时间限制:1000 ms  |  内存限制:65535 KB 难度:1   描述 Yougth和Hrdv玩一个游戏,拿出n个石子摆成一圈,Yougth和Hrdv分别从其中取石子,谁先取完 ...

  10. nim3取石子游戏 (威佐夫博弈)

    http://www.cnblogs.com/jackge/archive/2013/04/22/3034968.html 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有 ...

随机推荐

  1. C#元数据的概念,以及一个使用了lambda表达式的简单例子

    先看一个例子 假设你写了一个 C# 类库 MathUtils.dll: public class Calculator { public int Add(int a, int b) => a + ...

  2. 未给任务“SignFile”的所需参数“CertificateThumbprint”赋值.

    问题重现 一个项目发布时错误如下错误: 解决方法 打开项目属性-签名 方式一 [取消勾选]为 ClickOnce 清单签名 - 简单粗暴 方式二 [勾选]为 ClickOnce 清单签名 创建测试证书 ...

  3. 一文速通 Python 并行计算:05 Python 多线程编程-线程的定时运行

    一文速通 Python 并行计算:05 Python 多线程编程-线程的定时运行 摘要: 本文主要讲述了 Python 如何实现定时任务,主要有四种方式:通过 threading.Timer 类.通过 ...

  4. [T.2] 团队项目:选题和需求分析

    项目 内容 这个作业属于哪个课程 2025年春季软件工程(罗杰.任健) 这个作业的要求在哪里 T.2团队项目:选题和需求分析 团队在这个课程的目标是 学习软件工程相关知识,培养编程和团队协作能力,做出 ...

  5. ubuntu nginx + php7.2 + mysql5.7环境搭建

    一.换源 备份原来的源 sudo cp /etc/apt/sources.list /etc/apt/sources_init.list 更换源 sudo gedit /etc/apt/sources ...

  6. SpringBoot静态资源访问--转载

    转载地址:https://www.jianshu.com/p/d40ee98b84b5

  7. 基于Kubernetes可扩展的Selenium 并行自动化测试部署及搭建(3)——基于k8s的selenium grid集群搭建

    本篇主要讲解如何使用k8s搭建selenium grid集群 Selenium Grid集群部署 1.  首先我们将通过 Kubernetes 服务进行通信以到达hub和nodes.Kubernete ...

  8. Excel工具类之“参数汇总”

    一.SXSSFWorkbook技术 1.冻结行数 代码 SXSSFWorkbook wb = new SXSSFWorkbook(); SXSSFSheet sheet = wb.createShee ...

  9. RocketMQ半消息对消费者不可见是如何实现的?——事务消息机制揭秘

    首发于工号[BiggerBoy],原文链接 --"半消息藏在这里,但为什么你偷看也没用?" 上篇<RocketMQ系列笔记(三):消息模型与高阶玩法,顺序事务消息拿捏指南&g ...

  10. P2779 [AHOI2016初中组] 黑白序列题解

    题意: 小可可准备了一个未完成的黑白序列,用 B 和 W 表示黑色和白色,用 ? 表示尚未确定. 他希望知道一共有多少种不同的方法,在决定了每一个 ? 位置的颜色后可以得到一个小雪喜欢的黑白序列. 其 ...