1 条题解

  • 0
    @ 2025-8-24 21:46:47

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar C锥
    AFO

    搬运于2025-08-24 21:46:47,当前版本为作者最后更新于2020-09-03 21:08:42,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    九月四日 update:更改了错误的证明

    我发现其他题解都是用记忆化,蒟蒻表示看不懂,这里提供一种预处理 SG SG 值的代码。

    首先我们要知道 SG SG 定理: $ SG_{tot} = SG_1 \ xor \ SG_2\ xor \ SG_3 \ xor \ ... \ xor \ SG_n $ 。

    这里地方太小,我施展不开,就不证明了。(好吧太难了我不会)

    所以我们可以预处理出1到100000中每个数的 SG SG 值,对于每一个询问,我们只需求一下 SGtot SG_{tot} 就好了。

    那怎么预处理出每一个数的 SG SG 值呢?

    对于某一堆 i i ,有 i i 个石子,我们可以枚举分成的堆数 m(2mi) m (2 \le m \le i) 。设分成 m m 堆后, 每堆的石子个数为 xi x_i , 我们求出SG[xi] SG[x_i] 的异或和 res res ,就是分成 mm 堆后的状态的 SGSG 值,然后取 mexmex 运算得出当前状态 i i SG SG 值。

    可这太暴力了,时间复杂度是 O(n2) O(n^2) 的,70分,考虑优化。

    我们可以发现分成的 mm 堆的石子数最多有两种取值,分别是: i/m,(i/m)+1 i / m, (i / m) + 1 。这两种取值的个数分别是: i%m,mi%m i \% m , m - i\%m

    再考虑有贡献的值,因为求的是 SG SG 的异或和,所以只有某个取值的个数为奇数个时,才有贡献。接下来只需判断两种取值的个数的奇偶性就好了。

    举个例子:i=9 i = 9

    m :     2 3 4 5 6 7 8 9
    i / m : 4 3 2 1 1 1 1 1
    

    可以发现 m m 从5到9, i/m i / m 值是相同的,对于某些不同的 m m i/m i / m 的值相同,就可以用整除分块做。我们把后面分的石子堆数都列出来:

    5 : 2 2 2 2 1           
    6 : 2 2 2 1 1 1
    7 : 2 2 1 1 1 1 1
    8 : 2 1 1 1 1 1 1 1
    9 : 1 1 1 1 1 1 1 1 1
    

    可以发现某一个数目的石子堆数的奇偶性是一定的,证明:多举几个例子就好了

    im \begin{aligned}\frac{i} { m}\end{aligned} 为奇数时,$ \begin{aligned}m -i\%m = m - (i- m\times\lfloor \frac{i}{m}\rfloor)=m \times (1+\lfloor \frac{i}{m}\rfloor)-i\end{aligned} $,对于 $ \begin{aligned}1+\lfloor \frac{i}{m}\rfloor\end{aligned} $ 为偶数,m+1 m + 1 后,奇偶性不变

    im \begin{aligned}\frac{i} { m}\end{aligned} 为偶数时,$ \begin{aligned}i\%m = i- m\times\lfloor \frac{i}{m}\rfloor\end{aligned} $,m+1 m + 1 后,奇偶性不变 (感谢@Ame__ 的提醒qwq)

    所以对于 i/m i / m 相等的块内,分出的石子数的两种取值的奇偶性的组合只有两种(有点绕,好好想想),所以对于一整块内只需算出这两种不同的 SG SG 异或和就好了,因为取 mex mex 运算不需要判断重复的值。第一种和第二种一定可以算出所有不同的 SG SG 异或和的值,算前两种就好了。

    #include <bits/stdc++.h>
        
    using namespace std;
        
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
        
    const int N = 105, M = 1e5 + 5;
    int T, F, n;
    int a[N], SG[M], vis[N];
    
    int main() {
        
        T = read(); F = read();
    // 1 到 F-1 先手必输,SG数组的值为0
        for(int i = F;i <= M - 5; i++) {
            int r;
            for(int j = 2;j <= i; j = r + 1) {
                int t = i / j;
                r = i / (i / j);
                int flag;
                if(j == r) flag = 1; // 如果i/m值相等的只有一个数,那就不需要算两遍了
                else flag = 2;
                int num = 0, res = 0;
                while(num < flag) {
                    num++;
                    res = 0;
                    if((i % j) & 1) 
                        res ^= SG[t + 1];
                    if((j - i % j) & 1) 
                        res ^= SG[t];
                    j++;
                    vis[res] = i;
                }
            }
            for(int j = 0; ; j++) 
                if(vis[j] != i) { 
                    SG[i] = j; break;
                }
        }
    
        while(T --> 0) {
            n = read(); 
            int res = 0;
            for(int i = 1;i <= n; i++) {
                a[i] = read();
                res ^= SG[a[i]];
            }
            if(res == 0) printf("0 ");
            else printf("1 ");
        }
    
        return 0;
    }
    

    (如果哪有不对的还请dalao指正)

    • 1

    信息

    ID
    2308
    时间
    1000ms
    内存
    125MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者