Luogu2261 [CQOI2007]余数求和

题目蓝链

Description

定义函数\(G(n, k) = \sum\limits_{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\n", ans);

    return 0;
}
posted @ 2019-01-31 10:29 xunzhen 阅读(...) 评论(...) 编辑 收藏