[CQOI 2007] 余数求和

题目描述

原题戳我
求下面这个式子的值:

\[\sum _{i=1} ^{n} k\; mod\; i \]

题目解答

冷静分析

我们得搞清楚$ a_. mod_. b$的意义是什么,看下面:

\[a\; mod\; b=a-\left \lfloor \frac{a}{b} \right \rfloor \times b \]

那么原式化为:

\[\sum _{i=1} ^{n} k\; mod\; i=nk-\sum _{i=1} ^{n} \left \lfloor \frac{k}{i} \right \rfloor \times i \]

那么现在只需考虑\(\left \lfloor \frac{k}{i} \right \rfloor \times i\)了。基于向下取整的缘故以及\(i\)的可变性、连续性,易知在\(i\)某一段区间中会有重复出现的数,而这个区间的左右边界是可求的。
假设我们已经知道了某一连续区间的左边界\(s\),那么我们就能知道区间中的值\(v=\left \lfloor \frac{k}{s} \right \rfloor\),又因为右边界\(t\geq s\),且恰好是区间的结束点,那么\(t=\left \lfloor \frac{k}{v} \right \rfloor\)。这样一来,又可以根据\(t\)求出下一个区间的左边界\(t+1\)。区间求和用高斯求和法求和即可。
需要注意的是,\(t\)有可能比\(n\)大,或者\(v\)可能为\(0\)。前者将右边界调成\(n\)即可,后者中断循环即可。

代码一览

#include <cstdio>
using namespace std;
int main()
{
    long long int r, ans = 0, n, k, p;
    scanf("%lld%lld", &n, &k);
    ans = n * k;
    for(register int i = 1; i <= n; i = r + 1)
    {
        p = k / i;
        if(p)
            if(k / p > n)
                r = n;
            else
                r = k / p;
        else
            break;
        ans -= (i + r) * (r - i + 1) * p / 2;
    }
    printf("%lld", ans);
    return 0;
}

该算法的时间复杂度是\(O(\sqrt{k})\),洛谷上测评耗时0ms。

写在最后

感谢大家的关注和阅读。
本文章借鉴了少许思路,最后经过本人思考独立撰写此文章,如需转载,请注明出处。

posted @ 2018-05-13 15:48  孤独·粲泽  阅读(144)  评论(0编辑  收藏  举报