[Luogu 2261] CQOI2007 余数求和

[Luogu 2261] CQOI2007 余数求和

<题目链接>


这一定是我迄今为止见过最短小精悍的省选题了,核心代码 (4) 行,总代码 (12) 行,堪比小凯的疑惑啊。

这题一看暴力很好打,然而 (10^{9}) 的范围注定会卡掉暴力。

所以我们要用除法分块来优化。

由题意得:(ans = sum_{i=1}^{n} k mod i)

我们知道,(a mod b = a - b imes lfloor frac{a}{b} floor)

因此,(ans = sum_{i=1}^{n} k - i imes lfloor frac{k}{i} floor = nk - sum_{i=1}^{n} i imes lfloor frac{k}{i} floor)

我们用样例来打表找规律,发现 (lfloor frac{k}{i} floor) 分别在一定的区域内相等,如下表所示:

(i) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10)
(lfloor frac{k}{i} floor) (5) (2) (1) (1) (1) (0) (0) (0) (0) (0)

可见 (lfloor frac{k}{i} floor) 分成了 (3) 块,我们只需要计算 (n imes k) 减去每一块的和即可。

首先枚举块的左边界 (l),并根据左边界和 (k) 计算出右边界 (r)

(t = lfloor frac{k}{l} floor),分两种情况讨论:

  • (t eq 0),则 (r = min (lfloor frac{k}{t} floor , n))

  • (t = 0),则 (r = n)

(请自行打草稿验证。)

右边界有了,每一块的和也就可以计算出了。

每一块的和 (=) 当前块的 (t) ( imes) 当前块元素个数 ( imes) 当前块 (i) 的平均值 (= t imes (r-l+1) imes (l+r) div 2)

当前块处理完后,令 (l = r + 1),开始计算下一块,直到计算至 (n)

除法分块就是这样,在莫比乌斯反演优化中也有作用的。

给出最短小精悍的省选题代码。

记得开long long!

#include <algorithm>
#include <cstdio>
using std::min;
long long n,k,ans;
int main(int argc,char *argv[])
{
	scanf("%lld %lld",&n,&k);
	for(long long l=1,r,t;l<=n;l=r+1)
		r=(t=k/l) ? min(k/t,n) : n,ans-=t*(r-l+1)*(l+r)>>1;
	printf("%lld
",ans+n*k);
	return 0;
}

谢谢阅读。

原文地址:https://www.cnblogs.com/Capella/p/8476047.html