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

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. Excel百万数据如何快速导入?

    前言 今天要讨论一个让无数人抓狂的话题:如何高效导入百万级Excel数据. 去年有家公司找到我,他们的电商系统遇到一个致命问题:每天需要导入20万条商品数据,但一执行就卡死,最长耗时超过3小时. 更魔 ...

  2. Linux终端居然也可以做文件浏览器?

    大家好,我是良许. 在抖音上做直播已经整整 5 个月了,我很自豪我一路坚持到了现在[笑脸] 最近我在做直播的时候,也开始学习鱼皮大佬,直播写代码.当然我不懂 Java 后端,因此就写写自己擅长的 Sh ...

  3. C 图上的遍历算法

    图上的遍历算法 广度优先搜索 BFS 概念 广度优先搜索(Breadth-First Search)是一种图遍历算法,用于在图或树中按层次逐层访问节点.它从源节点(起始节点)开始,首先访问源节点的所有 ...

  4. 使用Python可视化洛伦兹变换

    引言 大家好!今天我们将探讨一个非常有趣且重要的物理概念-洛伦兹变换.它是相对论的核心内容之一,描述了在高速运动下,时间.长度以及其他物理量是如何发生变化的.通过使用 Python 进行可视化,我们不 ...

  5. Python合成多个视频为一个脚本

    编写背景: 由于线上用户反馈媒体添加页加载时间很长,猜测是由于本地视频内存过大引起,于是编写此脚本以便快速生成内存很大的视频 代码如下: # coding=utf-8 from moviepy.edi ...

  6. MySQL安装入门第一篇

    [1]MySQL的版本:近期主要历史版本有5.0/5.1/5.5/5.6/5.7,目前最新版本是MySQL8.6.0曾经是个内部试验版本,已取消了. MySQL8.0的版本历史 1) 2016-09- ...

  7. 看过源码吗?说下 Spring 由哪些重要的模块组成?

    是的,Spring源码可以深入分析,Spring框架是一个庞大的生态系统,包含多个模块,每个模块都为不同的功能提供支持.以下是Spring的主要模块及其职责: 1. Core Container(核心 ...

  8. Git Reset 彻底解析:--hard 模式操作步骤、风险与完整恢复指北

    结论先行 使用 git reset --hard <commit_id> 可强制将本地代码.暂存区.工作目录彻底回退到指定提交状态,但会丢弃目标提交之后的所有提交记录(需谨慎操作,尤其涉及 ...

  9. 【笔记】reko 0.10.2 反编译工具安装和使用记录|(2) user‘s guide

    Reko user's guide Reko是一个二进制可执行文件的反编译器.它接受输入的一个或多个二进制可执行文件,然后反编译成高级语言.它可以在GUI shell中被交互地使用,作为一个命令行项目 ...

  10. [随记]-linux侦听端口的4种方法

    侦听 192.168.0.1 服务器上的 10086 端口是否打开 1. telnet telnet是windows 内置的功能,当然 linux 也有.用法:  tenlet 192.168.0.1 ...