P2261 [CQOI2007]余数求和(整除分块)

P2261 [CQOI2007]余数求和(整除分块)

思路:整除分块。

G ( n , k ) G(n,k)

= i = 1 n k m o d i =\sum\limits_{i=1}^n k\bmod i

= i = 1 n k k i × i =\sum\limits_{i=1}^n k-\lfloor\dfrac{k}{i}\rfloor\times i

= n k i = 1 n k i × i =nk-\sum\limits_{i=1}^n\lfloor\dfrac{k}{i}\rfloor\times i

对于 x = k i x=\lfloor\dfrac{k}{i}\rfloor 相同的 i i ,是一个区间 [ l , r ] [l,r] ,因此我们可以进行分块运算。

对于当前区间左端点 l l ,它对于的 r = k k l r=\dfrac{k}{\lfloor\dfrac{k}{l}\rfloor}

即: i = l r k i × i = x i = l r i = x ( l + r ) ( r l + 1 ) 2 \sum\limits_{i=l}^r \lfloor\dfrac{k}{i}\rfloor\times i=x \sum\limits_{i=l}^r i=\dfrac{x(l+r)(r-l+1)}{2}

然后 l = r + 1 l=r+1 进入下一个块即可。

时间复杂度: O ( n ) O(\sqrt{n})

因为此题: k k 可能小于 n n 所以我们直接取到 m i n ( n , k ) min(n,k) 为止即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int main(){
	ll n,k;
	scanf("%lld%lld",&n,&k);
	ll ans=n*k;
	for(ll l=1,r;l<=min(n,k);l=r+1){
		r=min(k/(k/l),n);
		ans-=k/l*(r-l+1)*(l+r)>>1;
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/107741118