Luogu2261 [CQOI2007]余数求和

Posted xunzhen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu2261 [CQOI2007]余数求和相关的知识,希望对你有一定的参考价值。

题目蓝链

Description

定义函数(G(n, k) = sumlimits_{i = 1}^{n} k~mod~i),给定(n, k),求函数(G)的值

(n, k leq 10^9)

Solution

我一开始看这题的时候居然还懵了一下

因为当(i > k)时,余数一定都是(k),所以我们只需要考虑(i leq k)的部分怎么求

我们可以把(k)表示成(a cdot i + b)的形式,我们发现(k)分别整除([1, k])之间的数,最多只会有根号级别的商的个数

我在这里简单证明一下

当除数(leq sqrt k)时,因为除数只有(sqrt k)个,显然商也只会有(sqrt k)
当除数(gt sqrt k)时,因为商会小于(sqrt k),所以商最多也只有(sqrt k)

所以我们可以通过整除分块来解决这个问题

我们用整除分块找出所有(a)相同的(i)的区间,在这段区间内(b)一定会是一个公差为(a)的等差数列,可以直接(mathcal{O}(1))计算得

所以总的复杂度为(mathcal{O}(sqrt k))

Code

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define mp make_pair
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }

inline int read() {
    int sum = 0, fg = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == ‘-‘) fg = -1;
    for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    return fg * sum;
}

int main() {
#ifdef xunzhen
    freopen("math.in", "r", stdin);
    freopen("math.out", "w", stdout);
#endif

    int n = read(), m = read();

    LL ans = n > m ? (LL)m * (n - m) : 0;
    chkmin(n, m);

    for (int l = 1, r = 0; l <= n; l = r + 1) {
        r = min(m / (m / l), n);
        int r1 = m % r, r2 = m % l;
        ans += (LL)(r1 + r2) * ((r2 - r1) / (m / l) + 1) / 2;
    }
    printf("%lld
", ans);

    return 0;
}

以上是关于Luogu2261 [CQOI2007]余数求和的主要内容,如果未能解决你的问题,请参考以下文章

Luogu2261 [CQOI2007]余数求和

Luogu P2261 [CQOI2007]余数求和

Luogu P2261 [CQOI2007]余数求和

p2261 [CQOI2007]余数求和

题解 P2261 [CQOI2007]余数求和

P2261 [CQOI2007]余数求和