[CQOI2007] 余数求和
Description
求 \(\sum_{i=1}^n\;k\;mod\;i\)
Solution
先化简式子
\(\sum_{i=1}^n\;k\;mod\;i=\sum_{i=1}^nk-\lfloor{\frac{k}{i}}\rfloor=k\times n-\sum_{i=1}^n\lfloor{\frac{k}{i}}\rfloor\)
用样例打表找规律之后,发现 \(\lfloor{\frac{k}{i}}\rfloor\) 分别在一定的区域内相等。
所以用分块除法来做这题。
首先定义 \(t=\lfloor{\frac{k}{i}}\rfloor\)
用 \(l\) 来代表我们当前除法块的左边界, \(r\) 来代表右边界,我们让一开始的 \(l=1\) ,因为第一块除法块的开头永远都是从 \(1\) 开始,然后我们可以通过 \(l\) 来求出 \(r\) 。
t=k/l;
if(!t) break;
r=std::min(k/t,n);
因为我们的定义是 \(t=\lfloor{\frac{k}{i}}\rfloor\)
所以我们可以直接让 \(t=k/l\) 来求出他当前除法块的商,因为连续一块除法块的商都是相等的(不相等就绝对不是同一块),这样我们算出了 \(t\) ,然后就通过 \(t\) 来找 \(l\)
当 \(t=0\) 时,\(r=n\)。
当 \(t!=0\) 时,\(r=min(\lfloor{\frac{k}{t}}\rfloor,n)\)
解释如下,因为当 \(t=0\) 时,后面那一块肯定都是大于 \(k\) 的那一段,所以我们直接让 \(r=n\) ,算出最后这一大块,当 \(t!=0\) 的时候,我们让 \(r=\lfloor{\frac{k}{t}}\rfloor\),这样可以算出有这个整除的商的最大的一个整数是多少,因为 \(\lfloor{\frac{k}{t}}\rfloor\) 可能会大于 \(n\) ,所以我们加上一个 \(min\) 函数,防止他超过边界。 有了右边界,每一块的和也就好求了
剩下的就是用 \(n\times k\)减去每一块的值就好了
Code
#include<cstdio>
#include<iostream>
#define int long long
int ans;
int n,k;
signed main(){
int r;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i=r+1){
int t=k/i;
if(!t) break;
r=std::min(k/t,n);
ans+=t*(i+r)*(r-i+1)/2;
//printf("i=%lld,ans=%lld\n",i,ans);
}
printf("%lld\n",n*k-ans);
return 0;
}