题目大意
给出正整数 n 和 k 计算 (G(n, k)=k mod 1 + k mod 2 + k mod 3 + cdots + k mod n) 的值 其中 (k mod i) 表示 k 除以 i 的余数。
解析
整除分块的一个典型例子。
整除分块解决的是形如
[sum^n_{i=1} ~ lfloorfrac{n}{i}
floor
]
的问题,其复杂度为(O(sqrt{n}))。
实际上是规律性的一类问题,打表可以发现对于一些连续的(i),(lfloorfrac{n}{i} floor)具有相同的值。具体而言,对于一个(i),在一个区间(isim lfloorfrac{n}{lfloorfrac{n}{i} floor} floor)中,(lfloorfrac{n}{i} floor)具有相同的值。
也就是说,对于这个问题,我们只需要把整除分块中每一块的和累加就行了。
回到这道题,把题意转化为数学语言
[sum_{i=1}^n ~ k mod i
]
根据模算术的定义,可以写成
[sum_{i=1}^n ~ k-lfloorfrac{k}{i}
floor*i
]
即
[n*k-sum_{i=1}^n ~ lfloorfrac{k}{i}
floor*i
]
后面的东西就是整除分块,对于一个块(lsim r),有
[(r-l+1)*k-(r-l+1)lfloorfrac{k}{i}
floor*sum_{i=l}^r ~ i
]
所以对于一个块,(sum_{i=l}^r ~ i) 实际上是一个等差数列,我们一并求出来就可以了。
参考代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
ll n,k;
int main()
{
scanf("%d%d",&n,&k);
ll tmp=0;
for(int l=1,r=0;l<=n;l=r+1){
if(!(k/l)) r=n;
else r=min(k/(k/l),n);
tmp+=(r-l+1)*(k/l)*(l+r)/2;
}
printf("%lld
",n*k-tmp);
return 0;
}