洛谷P2261余数求和

传送门啦

再一次见证了分块的神奇用法,在数论里用分块思想。

我们要求 $ 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;
}
顺风不浪,逆风不怂。
原文地址:https://www.cnblogs.com/Stephen-F/p/9885948.html