P2261 [CQOI2007]余数求和 题解

题目传送门

在看到这道题后,首先想到直接暴力的做法,但显然在面对 \(10^9\) 时会 TLE 。

由此我们可以想到数论分块的做法。

出题人十分热心,在题干后加上了一句颇为显然但易被忽略的性质

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

形式化的,它表示 \(k\bmod i\Leftrightarrow k\ -\ \left\lfloor\dfrac{k}{i}\right\rfloor \times i\)

应用这一重要性质,我们可以对题中所给公式进行如下变形:

\[G(n,k)=\sum_{i=1}^nk\ -\ \left\lfloor\dfrac{k}{i}\right\rfloor \times i \]

再将 \(k\) 进行提取,可得:

\[G(n,k)=n\times k-\sum_{i=1}^n\left\lfloor\dfrac{k}{i}\right\rfloor \times i \]

可以发现,\(\left\lfloor\dfrac{k}{i}\right\rfloor\) 的得数呈块状,在写代码时注意枚举左右边界即可。

注意:可能有 \(\left\lfloor\dfrac{k}{i}\right\rfloor=0\) 的情况出现,请注意在代码实现过程中特判

View code:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ri register int
#define	il inline
#define int long long

int n,k,ans=0;

il ll read(){
	ll x=0,y=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')
			y=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*y;
}

signed main(){
	n=read(),k=read();
	ans=n*k;
	for(ri i=1,j;i<=n;i=j+1){
		if(k/i)
			j=min(k/(k/i),n);
		else
			j=n;
		ans-=(k/i)*(i+j)*(j-i+1)/2;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2021-07-14 19:35  BFNewdawn  阅读(37)  评论(0编辑  收藏  举报