参考链接
https://www.cnblogs.com/0xfffe/p/9648943.html
这个是莫比乌斯反演的前置姿势?
总之先来看一个柿子
[sumlimits_{i=1}^{n} leftlfloorfrac{n}{i} ight floor ]
考虑怎么快速求这玩意儿。
首先,下意识的用(n=10)打一张表,得到
10 5 3 2 2 1 1 1 1 1
再拿大一点的(n)试几次过后我们发现(leftlfloorfrac{n}{i}
ight
floor)的取值很少,且单调递减废话。
那么具体有多少种取值呢?
对于(i le sqrt{n}),(leftlfloorfrac{n}{i} ight floor)最多只有(sqrt{n})中取值(这是一个很松的上界)
对于(i > sqrt{n}),(leftlfloorfrac{n}{i} ight floor < sqrt{n}),也是(sqrt{n})的。
所以取值数量的上界是(2sqrt{n})。
那么考虑枚举每一段连续的取值。
对于(i),我们希望找到一个最大的(j),使得(1 le i le j le n),且(leftlfloorfrac{n}{i} ight floor = leftlfloorfrac{n}{j} ight floor)
下面令(k = leftlfloorfrac{n}{i} ight floor),那么有(ki+r=n),其中(0 le r < i)。考虑一个最大的(d),满足(leftlfloorfrac{n}{i+d} ight floor=k)。
这时候也有(k(i+d)+r'=n),显然此时(r'=r-kd)。
发现这时候(r')一定小于(i+d),那么只要满足(r' ge 0)就好了,此时(d)最大为(leftlfloorfrac{r}{k} ight floor)。
所以
即
这样([i,j])里的值是一样的,没算完一个区间将(i)变为(j+1),继续计算下一个就好了。
for(int l=1,r=0;l<=n;l=r+1) {
r=n/(n/l);
ans+=1ll*(n/l)*(r-l+1);
}
下面看一道例题[CQOI2007]余数求和
题目要算的是
变一下
对于后面的,数论分块的时候将固定的(leftlfloorfrac{n}{i} ight floor)乘上(i...j)的和就好了,要注意一些特殊情况。
#define s1(l,r) (1ll*(l+r)*(r-l+1)/2)
int main() {
int n,k; scanf("%d%d",&n,&k);
ll ans=1ll*n*k;
for(int l=1,r=0;l<=n;l=r+1) {
if(l<=k) r=min(n,k/(k/l));
else r=n;
ans-=k/l*s1(l,r);
}
printf("%lld
",ans);
return 0;
}