http://www.lydsy.com/JudgeOnline/problem.php?id=1257
题目
输入n,k,求\[\sum_{i=1}^n k\bmod i\]
$1\leqslant n,k\leqslant 10^9$
题解
这是一道neerc2005的题目
如果直接算肯定超时
$k\bmod i$可以写成
\[k-\left\lfloor \frac{k}{i}\right\rfloor\times i\]
打表会发现$\left\lfloor\frac{k}{i}\right\rfloor$有时候会重复
然后就是构造什么时候重复
需要用到
\[\left\lfloor\frac{k}{i}\right\rfloor\leqslant \left(\frac{k}{i}\right)\]
那么
\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor\leqslant\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left(\frac{k}{i}\right)}\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{\left\lfloor i\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]
\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor\geqslant\left\lfloor\frac{k}{\left(\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right)}\right\rfloor=\left\lfloor\left\lfloor\frac{k}{i}\right\rfloor\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]
所以
\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]
显然中间部分也应该相等,因为若$x<y$,那么
\[\left\lfloor x\right\rfloor\leqslant\left\lfloor y\right\rfloor\]
这样就可以加速一部分,但是能加速多少呢?
直接打表验证:
int main() { ll i=1; int cnt=0; while(i<int(1e9)) { cnt++; printf("%10lld", i); i=int(1e9)/i; i=int(1e9)/i; i++; } printf("\n%d\n", cnt); }
得到最坏情况只有63244次循环
那么,可以直接求和了
\[\sum_{i=1}^n k\bmod i=nk-\sum_{i=1}^n \left\lfloor\frac{k}{i}\right\rfloor\times i\]
对于每一段相等的,第二项是一个等差数列,可以过时限
虽然不会证明时间复杂度,但是这样可以省很多证明的时间
AC代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define REP(i,a,b) for(int i=(a); i<(b); i++) #define REPE(i,a,b) for(int i=(a); i<=(b); i++) #define PERE(i,a,b) for(int i=(a); i>=(b); i--) using namespace std; typedef long long ll; int main() { int n,k; scanf("%d%d", &n, &k); ll i=1, ans=(ll)n*k; while(i<=n) { if(i>k) { break; } else { int j=k/(k/i); if(j>n) j=n; ans-=(i+j)*(k/i)*(j-i+1)/2; i=j+1; } } printf("%lld\n", ans); }