P2261 [CQOI2007] 餘數求和 題解

FRZ_29發表於2024-08-20

題目傳送門

思路

數學

數學

非常經典的題目

注意到 \(k \bmod i = k - \lfloor k / i\rfloor * i\)

於是可以對題目進行轉化,從求 \(G(n, k) = \sum_{i = 1}^k n \bmod i\) 變為求 \(G(n, k) = n \times k - \sum_{i = 1}^n\lfloor k/i\rfloor \times i\),所以僅需求出 \(\sum_{i = 1}^n\lfloor k/i\rfloor \times i\) 即可。

定理一:\(\forall i \in [1, k]\)\(\lfloor k/i \rfloor\) 僅有 \(2\sqrt k\) 種可能。

證明:若 \(i \leq \sqrt k\),則 \(\lfloor k/i\rfloor \geq \sqrt k\),而若 \(i \gt \sqrt k\),則 \(\lfloor k/i\rfloor \lt \sqrt k\)
所以 \(\forall i \in [1, k]\)\(\lfloor k/i\rfloor\) 僅有 \(2\sqrt k\) 種可能。
證畢。

定理二:\(\forall i \in [x, \lfloor k/\lfloor k/x\rfloor\rfloor]\)\(\lfloor k/i\rfloor\) 相同。

證明:很顯然,\(i = \lfloor k/\lfloor k/x\rfloor\rfloor\) 是最大的使 \(k / i = k / x\) 的值,而 \(\lfloor k / i\rfloor\)\(i\) 的增大單調遞減,故 \(\forall i \in [x, \lfloor k/\lfloor k/x\rfloor\rfloor]\)\(\lfloor k/i\rfloor\) 相同。

對於一段 \([x, \lfloor k/\lfloor k/x\rfloor\rfloor]\),它的貢獻為 \(\sum_{i = x}^{\lfloor k/\lfloor k/x\rfloor\rfloor} \lfloor k/x \rfloor \times i\),這可以用等差數列公式快速求得。

因為僅有 \(2\sqrt k\) 種取值,時間複雜度為 \(O(\sqrt k)\)

程式碼

點選檢視程式碼
/*
  --------------------------------
  |        code by FRZ_29        |
  |          code  time          |
  |          2024/08/20          |
  |           17:29:36           |
  |             星期二            |
  --------------------------------
                                  */

#include <iostream>
#include <climits>
#include <cstdio>
#include <ctime>
typedef long long LL;

using namespace std;

void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
    x = 0; int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    x *= f; RD(arg...);
}

#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)

LL n, k, ans;

int main() {
//    freopen("read.in", "r", stdin);
//    freopen("out.out", "w", stdout);
//    time_t st = clock();
    RD(n, k);
    ans = n * k;

    for (int x = 1, gx; x <= n; x = gx + 1) {
        gx = k / x ? min(k / (k / x), n) : n;
        ans -= (k / x) * (x + gx) * (gx - x + 1) / 2;
    }

    printf("%lld", ans);
//    printf("\n%dms", clock() - st);
    return 0;
}

/* ps:FRZ弱爆了 */

一片春愁待酒澆。江上舟搖,樓上簾招。秋娘渡與泰娘橋,風又飄飄,雨又蕭蕭。
何日歸家洗客袍?銀字笙調,心字香燒。流光容易把人拋,紅了櫻桃,綠了芭蕉。

相關文章