1 条题解

  • 0
    @ 2025-11-10 23:12:28
    #include <bits/stdc++.h>
    #define ll long long
    #define ull __int128
    using namespace std;
    const int B = 4e7, N = 45;
    ll n, ans;
    ull exgcd(ull a, ull b, ull c, ull n) {
        if (!b)
            return a / c * (n + 1);
    
        if (a >= c || b >= c)
            return a / c * (n + 1) + b / c * n * (n + 1) / 2 + exgcd(a % c, b % c, c, n);
    
        ull m = (a + n * b) / c;
        return n * m - exgcd(c - a - 1, c, b, m - 1);
    }
    ll f[N];
    void solve(ll a, ll b, ll n) {
        for (int i = 0; i <= 40; i++)
            f[i] = exgcd(a, b, (1ll << i), n);
    
        for (int i = 0; i < 40; i++)
            ans ^= (((f[i] - f[i + 1] * 2) & 1) << i);
    }
    signed main() {
        cin >> n;
    
        for (int i = 1; i <= min(n, 1ll * B); i++)
            ans ^= n % i;
    
        for (ll l = B + 1, r; l <= n; l = r + 1) {
            r = n / (n / l);
            solve(n % r, n / l, r - l);
        }
    
        cout << ans;
        return 0;
    }
    

    求 $n \mod 1 \oplus n \mod 2 \oplus \dots \oplus n \mod n$ 的详细题解

    题目分析

    给定正整数 nn1n10121 \le n \le 10^{12}),计算异或和 $ans = (n \mod 1) \oplus (n \mod 2) \oplus \dots \oplus (n \mod n)$。
    直接暴力枚举 1n1 \sim n 计算显然不可行(nn101210^{12}),需结合数论分块、异或按位独立性、类欧几里得算法等数学工具优化。

    核心思路

    1. 异或的按位独立性

    异或运算的每一位相互独立:最终结果的第 kk 位为 11,当且仅当所有 nmodin \mod i 中第 kk 位为 11 的数的个数为奇数
    因此,可分别计算每一位 kk0k400 \le k \le 40,因 24010122^{40} \approx 10^{12})的贡献,再组合得到最终答案。

    2. 数论分块(整除分块)

    观察到 nmodi=ninin \mod i = n - i \cdot \lfloor \frac{n}{i} \rfloor。对于 ii 的一段区间 [l,r][l, r],若 ni\lfloor \frac{n}{i} \rfloor 恒等于 qq(即 qq 为定值),则 nmodi=nqin \mod i = n - q \cdot i,可批量处理该区间的异或贡献。

    • 小范围 i[1,B]i \in [1, B]B=4×107B=4 \times 10^7):直接暴力计算(时间可接受)。
    • 大范围 i[B+1,n]i \in [B+1, n]:用数论分块,每块 [l,r][l, r] 满足 r=nqr = \lfloor \frac{n}{q} \rfloorq=nlq = \lfloor \frac{n}{l} \rfloor),块内 qq 为定值,批量计算贡献。

    3. 块内异或贡献计算(solve函数)

    对块 [l,r][l, r],设 q=nlq = \lfloor \frac{n}{l} \rfloorm=rlm = r - li=l+ti = l + tt[0,m]t \in [0, m]),则:
    $n \mod i = n - q \cdot (l + t) = (n - q \cdot l) - q \cdot t = a - b \cdot t$,其中 a=nmodla = n \mod lb=qb = q
    需计算异或和 XORt=0m(abt)XOR_{t=0}^m (a - b \cdot t),转化为按位计算:对每一位 kk,计算 (abt)(a - b \cdot t)kk 位为 11 的个数的奇偶性。

    4. 类欧几里得算法(exgcd函数)

    kk 位为 11 等价于 abt2k\lfloor \frac{a - b \cdot t}{2^k} \rfloor 为奇数(因 x2k\lfloor \frac{x}{2^k} \rfloor 的二进制最低位即 xx 的第 kk 位)。
    需计算 $sum = \sum_{t=0}^m \lfloor \frac{a - b \cdot t}{c} \rfloor$(c=2kc=2^k),其奇偶性即该位贡献。用类欧几里得算法高效计算该求和(递归降低系数规模,复杂度 O(logmax(a,b,c))O(\log \max(a,b,c)))。

    算法步骤

    1. 小范围暴力计算i[1,min(n,B)]i \in [1, \min(n, B)],直接计算 nmodin \mod i 并异或到 ansans
    2. 大范围分块处理
      a. 对每个块 [l,r][l, r],计算 q=nlq = \lfloor \frac{n}{l} \rfloora=nmodla = n \mod lb=qb = qm=rlm = r - l
      b. 调用 solve(a, b, m),计算块内异或贡献并更新 ansans
    3. solve函数逻辑
      a. 对每一位 kk0k400 \le k \le 40),用 exgcd 计算 $f[k] = \sum_{t=0}^m \lfloor \frac{a - b \cdot t}{2^k} \rfloor$。
      b. 由 f[k]2f[k+1]f[k] - 2 \cdot f[k+1] 的奇偶性判断第 kk 位贡献(推导见下文),更新 ansans
    4. 输出 ansans

    关键推导

    为什么 f[k]2f[k+1]f[k] - 2 \cdot f[k+1] 的奇偶性是第 kk 位的贡献?

    对任意整数 xx,有:
    $\lfloor \frac{x}{2^k} \rfloor = 2 \cdot \lfloor \frac{x}{2^{k+1}} \rfloor + (x \text{ 的第 } k \text{ 位})$
    两边对 t[0,m]t \in [0, m] 求和:
    $\sum_{t=0}^m \lfloor \frac{a - b \cdot t}{2^k} \rfloor = 2 \cdot \sum_{t=0}^m \lfloor \frac{a - b \cdot t}{2^{k+1}} \rfloor + \sum_{t=0}^m (a - b \cdot t \text{ 的第 } k \text{ 位})$
    令 $cnt_k = \sum_{t=0}^m (a - b \cdot t \text{ 的第 } k \text{ 位})$,则:
    cntk=f[k]2f[k+1]cnt_k = f[k] - 2 \cdot f[k+1]
    cntkcnt_k 的奇偶性即第 kk 位对异或和的贡献(奇为 11,偶为 00)。

    代码解析

    1. 全局常量与变量

    #include <bits/stdc++.h>
    #define ll long long
    #define ull __int128  // 避免溢出,用于类欧求和
    using namespace std;
    const int B = 4e7, N = 45;  // B:小范围暴力阈值;N:二进制最大位数(40足够)
    ll n, ans;                  // ans:最终异或结果
    

    2. 类欧几里得求和(exgcd函数)

    计算 $\sum_{t=0}^n \lfloor \frac{a - b \cdot t}{c} \rfloor$(命名为exgcd因推导类似扩展欧几里得)。

    ull exgcd(ull a, ull b, ull c, ull n) {
        if (!b)  // 边界:b=0,式子为a/c,求和t从0到n,共n+1项
            return a / c * (n + 1);
    
        // 拆分系数:当a >=c或b >=c时,分离整数部分(不影响求和奇偶性,简化递归)
        if (a >= c || b >= c)
            return a / c * (n + 1) + b / c * n * (n + 1) / 2 + exgcd(a % c, b % c, c, n);
    
        // 类欧核心变换:交换变量,降低系数规模
        ull m = (a + n * b) / c;  // 等价于max_t floor((a - b*t)/c)
        return n * m - exgcd(c - a - 1, c, b, m - 1);
    }
    

    3. 块内异或贡献计算(solve函数)

    计算 XORt=0m(abt)XOR_{t=0}^m (a - b \cdot t),并更新全局ans。

    ll f[N];  // f[k]:sum_{t=0}^m floor((a - b*t)/2^k)
    void solve(ll a, ll b, ll n) {
        // 1. 计算每一位k对应的求和f[k]
        for (int i = 0; i <= 40; i++)
            f[i] = exgcd(a, b, (1ll << i), n);  // c=2^i
    
        // 2. 计算每一位的贡献,异或到ans
        for (int i = 0; i <= 40; i++) {
            // cnt_k = f[i] - 2*f[i+1],取奇偶性
            ll cnt = (f[i] - f[i+1] * 2) & 1;
            if (cnt) ans ^= (1ll << i);  // 第i位贡献为1
        }
    }
    

    4. 主函数(分块+暴力)

    signed main() {
        cin >> n;
    
        // 第一部分:小范围i ∈ [1, min(n, B)],直接暴力计算
        for (int i = 1; i <= min(n, 1ll * B); i++)
            ans ^= n % i;
    
        // 第二部分:大范围i ∈ [B+1, n],数论分块
        for (ll l = B + 1, r; l <= n; l = r + 1) {
            ll q = n / l;          // 块内n//i = q(定值)
            r = n / q;             // 块的右边界:最大i满足n//i = q
            ll a = n % l;          // a = n mod l = 块内t=0时的rem_i
            ll b = q;              // b = q
            ll m = r - l;          // t的范围:0 ~ m(i从l到r)
            solve(a, b, m);        // 计算该块的异或贡献
        }
    
        cout << ans;
        return 0;
    }
    

    算法标签

    • 数论分块(整除分块)
    • 异或按位处理
    • 类欧几里得算法
    • 数学推导
    • 暴力枚举(小范围优化)

    复杂度分析

    • 小范围暴力O(B)O(B)B=4×107B=4 \times 10^7,时间可接受(约1~2秒)。
    • 大范围分块:块数为 O(n)O(\sqrt{n})(因 q=n//iq = n//i 最多有 2n2\sqrt{n} 个不同值),对每个块:
      • solve函数:O(40×logM)O(40 \times \log M)MM 为类欧递归深度,约 log1012=40\log 10^{12} = 40)。
    • 总复杂度:O(B+n×40×40)O(B + \sqrt{n} \times 40 \times 40),对 n=1012n=10^{12} 可轻松通过。
    • 1

    信息

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