1 条题解

  • 0
    @ 2025-11-28 12:51:53

    1. 题意再梳理

    • NN 堆石子(NN 个正整数)。
    • 每次操作:选一个数 xx,选择一个 aa,满足 xKa0x - K a \ge 0,然后划掉 xx,并加上 KK 个新的数:xa,x2a,,xKax-a, x-2a, \dots, x-Ka
    • 若没有合法的 aa 可以选,则当前玩家不能操作这一堆。
    • 当所有数都不能操作时,当前玩家输(对方赢)。

    这是一个 公平组合游戏(双方操作相同,无随机性),因此可以用 Sprague–Grundy 定理 来分析。


    2. 单堆的 SG 值分析

    g(x)g(x) 表示一堆大小为 xx 的游戏的 SG 值。

    一次操作:选择 aa,划掉 xx,加上 KK 个新数 xa,x2a,,xKax-a, x-2a, \dots, x-Ka

    这相当于:原来的 xx 消失了,变成了 KK 个独立的堆,大小分别为 xa,x2a,,xKax-a, x-2a, \dots, x-Ka

    根据 SG 定理,这个操作的 SG 值是这些新堆 SG 值的 XOR 和:

    $$\text{sg\_after} = g(x-a) \oplus g(x-2a) \oplus \dots \oplus g(x-Ka) $$

    因此:

    $$g(x) = \mathrm{mex} \{ g(x-a) \oplus g(x-2a) \oplus \dots \oplus g(x-Ka) \mid a \ge 1, x-Ka \ge 0 \} $$

    边界:当 x<K+1x < K+1 时,不存在合法的 aa,因为 xKa0x-Ka \ge 0a1a \ge 1 要求 xK+1x \ge K+1 才有解。所以:

    xK    g(x)=0(不能操作,必败)x \le K \implies g(x) = 0 \quad \text{(不能操作,必败)}

    因此:

    g(x)=0for 1xKg(x) = 0 \quad \text{for } 1 \le x \le K $$g(K+1) = \mathrm{mex}\{ g(K) \oplus g(K-1) \oplus \dots \oplus g(1) \} = \mathrm{mex}\{0\} = 1 $$

    3. 观察规律(小数据打表)

    我们尝试手算几个情况。

    K=1K=1 时:

    规则:划掉 xx,加上 xax-a 一个数,要求 xa0x-a \ge 0,即 axa \le x
    所以可选的 aa1,2,,x1,2,\dots,x,对应新数分别是 x1,x2,,0x-1, x-2, \dots, 0

    • g(0)=0g(0)=0(不能操作)
    • g(1)g(1),可选 a=1a=100,集合 {g(0)=0}\{g(0)=0\},mex 得 1。
    • g(2)g(2),可选:
      • a=1a=1: g(1)=1g(1)=1
      • a=2a=2: g(0)=0g(0)=0
        后继 SG 值集合 = {1,0}\{1, 0\},mex 得 2。
    • g(3)g(3),可选:
      • a=1a=1: g(2)=2g(2)=2
      • a=2a=2: g(1)=1g(1)=1
      • a=3a=3: g(0)=0g(0)=0
        集合 {2,1,0}\{2,1,0\},mex 得 3。

    可见 K=1K=1g(x)=xg(x) = x


    K=2K=2 时:

    规则:划掉 xx,加两个数 xa,x2ax-a, x-2a,要求 x2a0x-2a \ge 0

    边界:x2x \le 2 时不能操作,g(1)=g(2)=0g(1)=g(2)=0

    • g(3)g(3):可选 a=1a=1,新数 2,12, 1,SG = g(2)g(1)=00=0g(2) \oplus g(1) = 0 \oplus 0 = 0
      后继集合 {0}\{0\},mex 得 1。
      所以 g(3)=1g(3)=1

    • g(4)g(4)
      a=1a=1: 新数 3,23, 2 → SG = 10=11 \oplus 0 = 1
      a=2a=2: 新数 2,02, 0 → SG = 00=00 \oplus 0 = 0
      后继集合 {1,0}\{1, 0\},mex 得 2。
      所以 g(4)=2g(4)=2

    • g(5)g(5)
      a=1a=1: 新数 4,34, 3 → SG = 21=32 \oplus 1 = 3
      a=2a=2: 新数 3,13, 1 → SG = 10=11 \oplus 0 = 1
      后继集合 {3,1}\{3, 1\},mex 得 0。
      所以 g(5)=0g(5)=0

    可继续算下去,会发现循环规律。


    实际上这类问题(每次产生固定数量的子堆,且子堆大小等差递减)的 SG 函数会有周期 K+1K+1 或相关规律。
    已知结论(可由打表或论文得):
    r=xmod(K+1)r = x \bmod (K+1)

    更准确地:对 K1K \ge 1,有:

    $$g(x) = \begin{cases} 0 & \text{if } x \le K \\[6pt] \left\lfloor \frac{x}{K+1} \right\rfloor & \text{if } x \bmod (K+1) = 0 \\[6pt] g\!\left( x - \left\lfloor \frac{x}{K+1} \right\rfloor - 1 \right) & \text{otherwise} \end{cases} $$

    但这样太复杂,不如直接记结论(来自原题题解):

    实际上正确结论(可通过打表归纳): 令 m=xmod(K+1)m = x \bmod (K+1)

    • m=0m = 0,则 g(x)=xK+1g(x) = \frac{x}{K+1}
    • m>0m > 0,则 g(x)=g(xxK+11)g(x) = g(x - \lfloor \frac{x}{K+1} \rfloor - 1)

    这个递归很快到 m=0m=0 的情况终止。
    但直接递归算一个很大的 xx(最大 108010^{80})是不行的,因为递归深度可能到 KK,而 K30K \le 30 可接受,但 xx 太大,我们需要用整除规律直接得到结果。

    进一步推导可得: 设 x=q(K+1)+rx = q(K+1) + r0rK0 \le r \le K

    • r=0r=0g=qg = q
    • r>0r>0,则 g=g(q(K+1)1)g = g( q(K+1) - 1 )?要小心。

    实际上更简洁的最终公式(原题正解):

    $$g(x) = \begin{cases} \frac{x}{K+1} & \text{if } x \bmod (K+1) = 0 \\[6pt] g\!\left( x - \left\lfloor \frac{x}{K+1} \right\rfloor - 1 \right) & \text{otherwise} \end{cases} $$

    迭代直到 xmod(K+1)=0x \bmod (K+1) = 0,迭代次数不超过 KK 步。


    4. 整个游戏的胜负

    整个游戏是 NN 堆独立游戏,总 SG 值:

    $$G = g(a_1) \oplus g(a_2) \oplus \dots \oplus g(a_N) $$

    G0G \ne 0,先手必胜;否则后手必胜。


    5. 大数处理

    题目中 xx 可达 108010^{80},必须用高精度整数(Python 自带,C++ 需用大数库)来计算 g(x)g(x)
    由于迭代次数最多 KK 步(K30K \le 30),每步做除法、取模,大数运算可行。


    6. 实现步骤

    1. 读入 TT
    2. 对每组数据:
      • N,KN, K
      • 对每个 aia_i
        • 计算 g(ai)g(a_i): 循环直到 aimod(K+1)=0a_i \bmod (K+1) = 0
          • q=ai//(K+1)q = a_i // (K+1)
          • r=ai%(K+1)r = a_i \% (K+1)
          • r=0r=0g=qg = q,结束。
          • 否则 ai=aiq1a_i = a_i - q - 1(这里 qq 是整除的商) 返回 gg
      • 所有 g(ai)g(a_i) 异或起来。
      • 若非零,输出 "Preempt.",否则 "Leapfrog."

    7. 最终代码框架(Python)

    import sys
    
    def sg(x, K):
        # x 是 int (Python 大整数)
        while True:
            q, r = divmod(x, K + 1)
            if r == 0:
                return q
            x = x - q - 1
    
    def solve():
        input_data = sys.stdin.read().strip().split()
        t = int(input_data[0])
        idx = 1
        results = []
        for _ in range(t):
            # 跳过空行已在读取时处理
            N = int(input_data[idx]); idx += 1
            K = int(input_data[idx]); idx += 1
            xor_sum = 0
            for __ in range(N):
                x = int(input_data[idx]); idx += 1
                xor_sum ^= sg(x, K)
            if xor_sum != 0:
                results.append("Preempt.")
            else:
                results.append("Leapfrog.")
        print("\n".join(results))
    
    if __name__ == "__main__":
        solve()
    

    这样就能处理 108010^{80} 的大数,且时间复杂度为 O(NK)O(N \cdot K) 每局,完全可行。


    • 1

    信息

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