【题解】[CQOI2007] 余数求和 By 5ab as a juruo.

题目信息

题目来源:CCF 重庆省选 2007;

在线评测地址:Luogu#2261

运行限制:时间不超过 \(1.00\ \textrm{s}\),空间不超过 \(128\ \textrm{MiB}\)

题目描述

给出正整数 \(n\)\(k\),请计算

\[G(n,k) = \sum\limits_{i=1}^n k\bmod i \]

其中 \(k\bmod i\) 表示 \(k\) 除以 \(i\) 的余数。

输入格式

输入只有一行两个整数,分别表示 \(n\)\(k\)

输出格式

输出一行一个整数表示答案。

数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n,k\le 10^3\)
  • 对于 \(60\%\) 的数据,保证 \(n,k\le 10^6\)
  • 对于 \(100\%\) 的数据,保证 \(1\le n,k\le 10^9\)

分析

这道题又是一个十分经典的数论题。

\(60\texttt{ pts}\)

直接暴力乱搞即可。

\(100\texttt{ pts}\)

说实话这道题数据有那么亿点水。

我们来推个式子:

\[\begin{aligned} G(n,k)= & \sum\limits_{i=1}^n k\bmod{i}\\ =& \sum\limits_{i=1}^n\left(k-\left\lfloor\dfrac{k}{i}\right\rfloor\right)\\ =&\, nk-\sum\limits_{i=1}^n\left\lfloor\dfrac{k}{i}\right\rfloor \end{aligned} \]

后面那一半是经典的整数分块,直接 \(\mathcal{O}(\sqrt{k})\) 就可以解决。所以这道题的数据范围应该开到 1e14 然后再取模的吧

怎么证明整数分块的复杂度?

对于 \(\left\lfloor\dfrac{k}{i}\right\rfloor\le \sqrt{k}\) 的,值必然最多只有 \(\sqrt{k}\) 个,所以这一部分是 \(\mathcal{O}(\sqrt{k})\) 的。

对于 \(\left\lfloor\dfrac{k}{i}\right\rfloor > \sqrt{k}\) 的,则 \(\dfrac{k}{i}>\sqrt{k}\),可以得到 \(i<\dfrac{k}{\sqrt{k}}=\sqrt{k}\),即这样的数的个数最多只有 \(\sqrt{k}\) 个,一样是 \(\mathcal{O}(\sqrt{k})\) 的。

所以整体的复杂度是 \(\mathcal{O}(\sqrt{k})\)

注意事项

因为特殊的原因,这道题请将所有变量开 long long,否则会出大问题。

同时,别忘了整数分块中对 \(r\) 变量上界的把握。

Code

#include <cstdio>
using namespace std;

typedef long long ll;

ll my_min(ll a, ll b) { return (a < b)? a:b; } // 最小值函数

int main()
{
	ll l, r, n, k, ans;

	scanf("%lld%lld", &n, &k); // 输入
	ans = 1ll * n * k;

	for (l = 1; l <= n; l = r + 1) // 分块
	{
		if (k / l)
			r = my_min(k / (k / l), n); // 别忘了取最小值,否则只有 50 分
		else
			r = n;
		
		ans -= 1ll * (k / l) * (l + r) * (r - l + 1) / 2; // 统计进去
	}

	printf("%lld\n", ans); // 输出

	return 0; // 然后就 AC 了、
}
posted @ 2020-09-05 16:43  5ab  阅读(88)  评论(0编辑  收藏  举报