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