BZOJ 1257 余数之和

http://www.lydsy.com/JudgeOnline/problem.php?id=1257

题目

输入n,k,求\[\sum_{i=1}^n k\bmod i\]

$1\leqslant n,k\leqslant 10^9$

题解

这是一道neerc2005的题目

如果直接算肯定超时

$k\bmod i$可以写成

\[k-\left\lfloor \frac{k}{i}\right\rfloor\times i\]

打表会发现$\left\lfloor\frac{k}{i}\right\rfloor$有时候会重复

然后就是构造什么时候重复

需要用到

\[\left\lfloor\frac{k}{i}\right\rfloor\leqslant \left(\frac{k}{i}\right)\]

那么

\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor\leqslant\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left(\frac{k}{i}\right)}\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{\left\lfloor i\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]

\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor\geqslant\left\lfloor\frac{k}{\left(\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right)}\right\rfloor=\left\lfloor\left\lfloor\frac{k}{i}\right\rfloor\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]

所以

\[\left\lfloor\frac{k}{\left\lfloor\frac{k}{\left\lfloor\frac{k}{i}\right\rfloor}\right\rfloor}\right\rfloor=\left\lfloor\frac{k}{i}\right\rfloor\]

显然中间部分也应该相等,因为若$x<y$,那么

\[\left\lfloor x\right\rfloor\leqslant\left\lfloor y\right\rfloor\]

这样就可以加速一部分,但是能加速多少呢?

直接打表验证:

int main() {
	ll i=1;
	int cnt=0;
	while(i<int(1e9)) {
		cnt++;
		printf("%10lld", i);
		i=int(1e9)/i;
		i=int(1e9)/i;
		i++;
	}
	printf("\n%d\n", cnt);
}

得到最坏情况只有63244次循环

那么,可以直接求和了

\[\sum_{i=1}^n k\bmod i=nk-\sum_{i=1}^n \left\lfloor\frac{k}{i}\right\rfloor\times i\]

对于每一段相等的,第二项是一个等差数列,可以过时限

虽然不会证明时间复杂度,但是这样可以省很多证明的时间

AC代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define REPE(i,a,b) for(int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
int main() {
	int n,k; scanf("%d%d", &n, &k);
	ll i=1, ans=(ll)n*k;
	while(i<=n) {
		if(i>k) {
			break;
		} else {
			int j=k/(k/i);
			if(j>n) j=n;
			ans-=(i+j)*(k/i)*(j-i+1)/2;
			i=j+1;
		}
	}
	printf("%lld\n", ans);
}

猜你喜欢

转载自www.cnblogs.com/sahdsg/p/12708261.html