「Gym-102803H」Hate That You Know Me

题目概述

给定 \(a, b, n\)\(a, b \in \{0, 1, 2, 3\} ,n \leq 10^{12}\) ,求

\[(\sum_{i = 1}^{n} \sum_{d|n} d^a \oplus \sum_{i = 1}^{n} \sum_{d|n} d^b) \]

思路&&做法

首先观察到题目需要求从 \(1\)\(n\) 的每个数的因数的 \(k\) 次方和,对于这种题,我们有一个思考方向是考虑计算贡献。

可以发现对于 \(x\) ,它会在它的倍数的地方计算一次贡献,假设当前是求 \(k\) 次方,则 \(x\) 的贡献就是 $x^k \times \left \lfloor \frac{n}{x} \right \rfloor $ 。

但我们显然不能枚举 \(x\) ,所以要思考怎么计算这个式子。结合式子中的向下取整以及 \(n \leq 10^{12}\) 的范围,我们可以联想到整除分块,对于整除分块,我们可以分出 \(O(\sqrt{n})\) 个 $ \left \lfloor \frac{n}{x} \right \rfloor $相等的块,并且每个块中的 \(x\) 是连续的,假设某一个块的区间是 \([l, r]\) ,该区间的贡献即 \(\left \lfloor \frac{n}{x} \right \rfloor \times \sum_{i = l}^r i^k\) ,注意到本题 \(a, b \le 3\) ,所以是有区间 \(k\) 次方和的公式的,这时我们只需动动小手,上网一查即可。

  • \(\sum_{i = l}^{r} i^0 = r - l + 1\)
  • \(\sum_{i = l}^{r} i^1 = \frac{(r + l) \times (r - l + 1)}{2}\)
  • \(\sum_{i = 1}^{n} i^2 = \frac{n \times (n + 1) \times (2n + 1)}{6}\)
  • \(\sum_{i = 1}^{n} i^3 = (\frac{n \times (n + 1)}{2})^2\)

对于 \(k\) 次方和,有兴趣可以看下这篇博客

/*
address:https://vjudge.net/problem/Gym-102803H

*/
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e6 + 5;
int a, b;
ULL n;
inline ULL calc(ULL l, ULL r, int x) {
    if (x == 0) return r - l + 1;
    if (x == 1) return (l + r) * (r - l + 1) / 2;
    if (x == 2) return (r * (r + 1) * (2 * r + 1) / 6) - ((l - 1) * (l) * (l * 2 - 1) / 6);
    if (x == 3) return (r * (r + 1) / 2) * (r * (r + 1) / 2) - (l * (r - 1) / 2) * (l * (r - 1) / 2);
}
int main() {
    scanf("%d%d%llu", &a, &b, &n);
    ULL x = 0, y = 0;
    for (ULL L = 1, R = 1;n / L;L = R + 1) {
        R = n / (n / L);
        x += calc(L, R, a) * (n / L);
        y += calc(L, R, b) * (n / L);
    }
    printf("%llu", x ^ y);
    return 0;
}

完结撒花……了吗?

如果此时你交一发,会发现自己神奇的 \(\text{WA}\) 在了第 \(3\) 个点。

仔细观察,代码中计算的部分是有除法的,在取模的情况下不能进行除法,然后我们又悲催地发现模数不是质数,还不保证互质,所以乘法逆元不可行。但我们知道,是在乘完取模后不能除,而我们在除的时候是在计算一个固定值,并不是边乘边取,所以我们可以在乘上两个数取模之前先把要除的数除掉就行。

/*
address:https://vjudge.net/problem/Gym-102803H
AC 2025/2/8 14:53
*/
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e6 + 5;
int a, b;
ULL n;
inline ULL calc(ULL l, ULL r, int x) {
    ULL ret = 1;
    if (x == 0) return r - l + 1;
    if (x == 1) {
        ULL x = l + r, y = r - l + 1;
        if (!(x & 1)) x /= 2;
        else y /= 2;
        return x * y;
    }
    if (x == 2) {
        ULL x = r, y = r + 1, z = 2 * r + 1;
        if (x % 2 == 0) x /= 2;
        else if (y % 2 == 0) y /= 2;
        else z /= 2;
        if (x % 3 == 0) x /= 3;
        else if (y % 3 == 0) y /= 3;
        else z /= 3;
        ret = x * y * z;
        x = l - 1, y = l, z = l * 2 - 1;
        if (!(x & 1)) x /= 2;
        else if (!(y & 1)) y /= 2;
        else z /= 2;
        if (x % 3 == 0) x /= 3;
        else if (y % 3 == 0) y /= 3;
        else z /= 3;
        ret -= x * y * z;
        return ret;
    }
    if (x == 3) {
        if (r & 1) ret = ((r + 1) / 2) * ((r + 1) / 2) * r * r;
        else ret = (r / 2) * (r / 2) * (r + 1) * (r + 1);
        ULL del;
        if (l & 1) del = ((l - 1) / 2) * ((l - 1) / 2) * l * l;
        else del = (l / 2) * (l / 2) * (l - 1) * (l - 1);
        ret -= del;
        return ret;
    }
}
int main() {
    scanf("%d%d%llu", &a, &b, &n);
    ULL x = 0, y = 0;
    for (ULL L = 1, R = 1;n / L;L = R + 1) {
        R = n / (n / L);
        x += calc(L, R, a) * (n / L);
        y += calc(L, R, b) * (n / L);
    }
    printf("%llu", x ^ y);
    return 0;
}

这道题不难,就是中间要在取模前除有点坑。

posted @ 2025-02-08 15:33  keysky  阅读(14)  评论(0)    收藏  举报