传送门啦
再一次见证了分块的神奇用法,在数论里用分块思想。
我们要求 $ ans = sumlimits ^{n} _{i=1} (k % i) $ ,如果我没看错,这个题的暴力有 $ 60 $ 分,当然,不甘平凡的我们怎么能为 $ 60 $ 分折腰,我们来看正解打法。
我们要知道 $ a % b = a-b*lfloorfrac{a}{b} floor$ 。.
我们代入后得到:
$ ans = sumlimits^{n}{i=1}(k-ilfloorfrac{k}{i} floor) = n*k-sumlimits^{n}{i=1}(i * lfloorfrac{k}{i} floor) $
然后我们用分块做,我们最后计算 $ n * k $ 减去每一段的和就好了。
开始枚举左端点,根据 $ k $ 计算出右端点:
若 $ lfloorfrac{k}{l} floor eq 0 $ , $ r = min(lfloorfrac{k}{lfloorfrac{k}{l} floor} floor , n) $ 。
若$ lfloorfrac{k}{l} floor=0 $ ,$ r=n $。
最后计算每一块的和:
$ sum = lfloorfrac{k}{l}
floor * (r-l+1) *(l+r) / 2$。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline long long read(){
char ch = getchar();
long long f = 1 , x = 0;
while(ch > '9' || ch < '0'){if(ch == '-')f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
return x * f;
}
long long n,k;
long long ans;
int main(){
n = read(); k = read();
ans = n * k;
for(int l=1,r;l<=n;l=r+1){
if(k / l != 0) r = min(k / (k / l) , n);
else r = n;
ans -= (k / l) * (r - l + 1) * (l + r) / 2;
}
printf("%lld
",ans);
return 0;
}