天哥

V1

2023/05/13阅读:7主题:蓝莹

CQOI 2007 余数之和 题解

CQOI 2007 余数之和

题意

,其中

思路

因为 ,所以 易求,专门考虑该式后半部分

以下过程与《算法竞赛进阶指南》有差别。

不难发现 单调不上升,也就是说, 的同一个值一定会连续出现。

不妨设 的取值范围为 ,那么应用等差数列求和公式可得

为了计算的方便,枚举 而不是 。枚举从 开始,每算完一段就令 。显然 ,难点在于求出

关于 的取值,下面给出较清晰的推导,方便理解。

  • 根据我们对右端点 的定义以及数列的单调不升性, 。根据向下取整的定义, 为整数且
  • 对于 这部分,有 ,而 都是正数,则
  • 对于 这部分,因为 都是整数,所以 。又因为 ,所以 。进一步得到
  • 上述两个结论连起来,得到 。又因为 为整数,所以

最后发现,此处得到的结论与书中结论相同。

实现

NowCoder 4ms。Luogu 40ms,单点3~5ms。(似乎两个网站用时计算方法不一样。)

注意边界。因为 的情况可能存在,需要 r = min(k / x, n); 一下。

代码中另外有一点解释一下。我在 for 循环的判断条件中,加入书中没有的 l <= k。这是因为当 时,显然 等于 ,没有必要再算了。

必须注意 long long 的问题,l, r, x 不开 long long 会WA。

#include <cstdio>
#include <algorithm>

using namespace std;

long long n, k, ans;

int main() {
    scanf("%lld%lld", &n, &k);
    ans = n * k;
    long long l, r, x;
    for (l = 1; l <= n && l <= k; l = r + 1) {
        x = k / l;
        r = min(k / x, n);
        ans -= x * (l + r) * (r - l + 1) / 2;
    }
    printf("%lld\n", ans);
    return 0;
}

写在最后

虽然基于教材做例题,但是推导还是必须自己推导的。

似乎自己这样的推导在思路上比较容易想到。当然在做题过程中还是要用 之类的小数据试一下的。

写题解时书不在身边,印象中书中只说明了从 除法取整的结果相同,但是没有说明 结果不同。

据说这题的方法有个专门的名字“分块除法”。该算法实际上就是根据整除的结果分成多个部分,每个部分分别计算。最典型的分块除法问题就是本题中

分类:

后端

标签:

C++

作者介绍

天哥
V1