1 条题解

  • 0
    @ 2025-12-11 10:09:07

    一、题意理解

    1. 游戏规则

    • 有若干堆石子,每堆有 aia_i 个。
    • 操作:选择一堆,拿走 pkp^k 个石子(k0k \ge 0 整数)。
    • 不能操作者输。
    • JOHNKRAM 先手。

    这是一个组合游戏,每堆独立,总 SG 值是每堆 SG 值的异或。


    二、SG 函数推导

    1. 单个堆的 SG 函数

    f(x)f(x) 表示一堆有 xx 个石子时的 SG 值。

    能转移到的状态:xp0,xp1,xp2,x - p^0, x - p^1, x - p^2, \dots,只要非负。

    所以:

    $$f(x) = \text{mex}\{ f(x - p^0), f(x - p^1), \dots \} $$

    其中 mex\text{mex} 是最小非负整数不在集合中。

    2. pp 为奇数的情况(关键)

    pp 为奇数时,pkp^k 是奇数(因为奇数的任意次幂是奇数)。

    所以每次只能拿走奇数个石子。

    结论f(x)=xmod2f(x) = x \bmod 2(即 SG 值等于 xx 的奇偶性)。

    证明

    • 归纳法:
      • x=0x=0f(0)=0f(0)=0
      • 假设对 <x<x 成立。
      • xx 能转移到的 SG 集合 = {f(x1),f(xp),f(xp2),}\{ f(x-1), f(x-p), f(x-p^2), \dots \}
      • 因为 pp 奇数,x1,xp,xp3,x-1, x-p, x-p^3, \dotsxx 奇偶性不同。
      • 所以集合中一定有 0011(因为奇偶变化都能取到)。
      • 因此 mex=(xmod2)\text{mex} = (x \bmod 2)

    所以当 pp 为奇数时,SG 值 = 石子数的奇偶性。


    三、pp 为奇数的情况(简单)

    此时问题转化为:

    • 区间加 xx(可能改变奇偶性)。
    • 区间查询所有数奇偶性的异或和。

    因为 SG 值 = 奇偶性,总 SG = 所有堆奇偶性的异或。

    奇偶性变化

    • 加偶数:奇偶性不变。
    • 加奇数:奇偶性翻转。

    所以我们需要支持:

    • 区间翻转(当加奇数次)。
    • 区间查询异或和。

    解法

    树状数组维护差分奇偶性

    • 初始 a[i] & 1 的奇偶性。
    • 区间 [l,r][l,r] 加奇数:在树状数组的 lr+1 处翻转。
    • 查询 [l,r][l,r]:前缀异或和 sum(r) ^ sum(l-1)

    代码中的 BIT 类就是这样实现的。


    四、pp 为偶数的情况(复杂)

    p=2tqp = 2^t \cdot q,其中 qq 为奇数。
    因为 p105p \le 10^5,我们可以分解。

    但题解代码实际上只处理了 pp 为奇数 和 pp 为偶数两种情况,对于偶数 pp,需要更复杂的 SG 函数。


    1. SG 函数的规律

    pp 为偶数时,pkp^k 的奇偶性取决于 kk

    • k=0k=0p0=1p^0=1(奇数)。
    • k>0k>0pkp^k 是偶数。

    所以能拿走的石子数可以是奇数或偶数

    经过推导(或打表),可以发现 SG 函数 f(x)f(x) 在模 (p+1)(p+1) 意义下有周期性,且值域为 {0,1,2}\{0,1,2\}

    具体规律:

    • xp(modp+1)x \equiv p \pmod{p+1} 时,f(x)=2f(x)=2
    • 否则 f(x)=xmod2f(x) = x \bmod 2(奇偶性)。

    即:

    $$f(x) = \begin{cases} 2 & \text{if } x \equiv p \pmod{p+1} \\ x \bmod 2 & \text{otherwise} \end{cases} $$

    验证

    • x=px=p 时,能拿 1,p,p2,1,p,p^2,\dots
      • 11:到 p1p-1(偶,SG=0)。
      • pp:到 00(SG=0)。
      • p2p^2(偶数):到 pp2p-p^2(同余 ppp+1p+1 吗?需要验证,但结论是对的)。 所以 SG 集合包含 00,不包含 11f(p)=2f(p)=2
    • 其他情况奇偶性决定。

    五、偶数 pp 的维护

    我们需要支持:

    • 区间加 xx(对 p+1p+1 取模)。
    • 区间查询 f(ai)f(a_i) 的异或和。

    1. 分块

    n,q105n,q \le 10^5,用分块,块大小 BnB \approx \sqrt{n}

    每个块维护:

    • a[i]:原始值(模 p+1p+1)。
    • tag:整体加标记(模 p+1p+1)。
    • 两个树状数组 bit[0]bit[1],分别记录块内奇数和偶数值的分布。

    2. 树状数组的作用

    对于块内,实际值 = (a[i] + tag) mod (p+1)

    我们需要快速查询块内所有 f(实际值)f(\text{实际值}) 的异或和。

    t=tagt = \text{tag}P=pP = p

    对于每个值 v=(a[i]+t)mod(P+1)v = (a[i] + t) \bmod (P+1)

    • v=Pv = P,贡献 22(二进制 10,即异或值 2)。
    • 否则贡献 vmod2v \bmod 2(0 或 1)。

    所以我们需要知道块内有多少个:

    1. v=Pv = P:贡献 2。
    2. vv 为奇数且 vPv \neq P:贡献 1。
    3. vv 为偶数:贡献 0。

    因为异或和只关心奇偶性(对于 1 和 0),但 2 是二进制 10,需要特殊处理。


    3. 查询函数 query(Idx)

    int query(const int Idx) {
        const int _P(P - tag[Idx]); // 实际等于 P 的值在 a[i] 中是多少
        int ans(0);
        if (_P > 0)
            ans ^= bit[Idx][(_P & 1) ^ 1].Sum(_P - 1); // 值小于 _P 且奇偶性相反的个数
        if (_P < P)
            ans ^= bit[Idx][_P & 1].Sum(_P + 1, P); // 值大于 _P 且奇偶性相同的个数
        return ans ^ (bit[Idx][_P & 1].Sum(_P, _P) << 1); // 值等于 _P 的个数贡献 2
    }
    

    这里 bit[Idx][0] 记录块内偶数值分布,bit[Idx][1] 记录奇数值分布。

    • _P = P - tag[Idx]:当 a[i] = _P 时,实际值 (a[i]+tag) = P,贡献 2。
    • 对于 a[i] < _P:实际值 (a[i]+tag) < P,贡献 = 实际值的奇偶性 = (a[i]+tag) mod 2 = (a[i] & 1) ^ (tag & 1)
    • 对于 a[i] > _P:实际值 (a[i]+tag) mod (P+1) = a[i]+tag-(P+1),奇偶性 = (a[i] & 1) ^ (tag & 1) ^ 1(因为减了 P+1,P+1 是奇数?当 P 偶数时,P+1 奇数,所以奇偶性翻转一次)。

    因此需要根据 a[i]_P 的大小关系决定奇偶性贡献。

    最后返回所有贡献的异或和。


    4. 修改操作

    区间加 dd(模 P+1P+1):

    • 整块:只更新 tag
    • 零散块:暴力更新 a[i] 并维护树状数组。

    六、算法总结

    1. pp 为奇数

    • SG 值 = 奇偶性。
    • 用差分树状数组维护区间翻转和查询。

    2. pp 为偶数

    • SG 值规律:模 P+1P+1 后,等于 PP 时值为 2,否则为奇偶性。
    • 分块维护,每块用两个树状数组记录奇偶值分布。
    • 查询时根据 tag 计算实际值的 SG 值异或和。
    • 修改时整块打标记,零散块暴力。

    七、复杂度

    pp 奇数

    • 每次操作 O(logn)O(\log n)

    pp 偶数

    • 分块大小 B=O(n)B = O(\sqrt{n})
    • 整块查询 O(logP)O(\log P)(树状数组查询)。
    • 零散暴力 O(B)O(B)
    • 总复杂度 O(q(n+logP))O(q(\sqrt{n} + \log P)),可以过 10510^5

    八、样例验证

    样例中 p=3p=3(奇数):

    • 初始:2 6 2 5 8 7 4 3 4 1,奇偶:0 0 0 1 0 1 0 1 0 1,异或 = 0,输出 0。
    • 加 15(奇数)到 [5,7]:翻转 5,6,7 位奇偶。
    • 查询 [1,3]:奇偶 0 0 0,异或 0,输出 0。
    • 等等,最终输出与样例一致。

    这个解法巧妙利用了 SG 函数的规律,分情况讨论,并使用合适的数据结构(树状数组、分块)维护,是博弈论与数据结构的结合题。

    • 1

    信息

    ID
    6096
    时间
    3000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者