20200223 专题:数论分块(咕~~~)

总览:

解决一类求和问题 ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^{n}{\lfloor \frac {n}{i} \rfloor} i=1nin
发现一段 i i i ⌊ n i ⌋ \lfloor \frac {n}{i} \rfloor in的值不变,于是可以分块求和
假设每一块的左右区间是 [ l , r ] [l,r] [l,r],则知道 l l l后, r = n n / l r=\frac {n}{n/l} r=n/ln(均为整除)
证明
那么设两个指针 l , r l,r l,r,每次 l = r + 1 , r = n n / l l=r+1,r=\frac {n}{n/l} l=r+1,r=n/ln后再数量乘以值累加即可

模板:

for(int l=1,r=0;l<=n;l=r+1){
		if(n/l==0)	break;
		r=n/(n/l);//如果循环为k,写成min(k/(k/l),n);
		ans+=(n/l)*(r-l+1)/2;
	}

时间复杂度: O ( n ) O(\sqrt n) O(n )
在这里插入图片描述
所以时间复杂度为 O ( n ) O(\sqrt n) O(n )

T1 P2261 [CQOI2007]余数求和

[CQOI2007]余数求和
题目描述
给出正整数 n n n k k k 计算 G ( n , k ) = k     m o d     1 + k     m o d     2 + k     m o d     3 + ⋯ + k     m o d     n G(n, k)=k\ \bmod\ 1 + k\ \bmod\ 2 + k\ \bmod\ 3 + \cdots + k\ \bmod\ n G(n,k)=k mod 1+k mod 2+k mod 3++k mod n 的值 其中 k     m o d     i k\ \bmod\ i k mod i 表示 k k k 除以 i i i 的余数。

例如 G ( 10 , 5 ) = 5     m o d     1 + 5     m o d     2 + 5     m o d     3 + 5     m o d     4 + 5     m o d     5 ⋯ + 5     m o d     10 = 0 + 1 + 2 + 1 + 0 + 5 + 5 + 5 + 5 + 5 = 29 G(10, 5)=5\ \bmod\ 1 + 5\ \bmod\ 2 + 5\ \bmod\ 3 + 5\ \bmod\ 4 + 5\ \bmod\ 5 \cdots + 5\ \bmod\ 10 =0+1+2+1+0+5+5+5+5+5=29 G(10,5)=5 mod 1+5 mod 2+5 mod 3+5 mod 4+5 mod 5+5 mod 10=0+1+2+1+0+5+5+5+5+5=29

输入格式
两个整数 n n n k k k

输出格式
答案一行

输入输出样例
输入
10 5
输出
29

说明/提示
100 % : n , k ≤ 1 0 9 100\%: n , k \le 10^9 100%:n,k109

思路:
a % b = a − a / b × b a\%b=a-a/b\times b a%b=aa/b×b
G ( n , k ) = k × k − ⌊ a b ⌋ × b G(n,k)=k\times k-\lfloor \frac{a}{b} \rfloor \times b G(n,k)=k×kba×b
数论分块,对于一个块 [ l , r ] [l,r] [l,r]
⌊ a b ⌋ \lfloor \frac{a}{b} \rfloor ba为定值, ∑ b \sum b b为块中数的和,为 ( l + r ) × ( r − l + 1 ) 2 \frac {(l+r)\times (r-l+1)}{2} 2(l+r)×(rl+1)

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();x<'0'||x>'9';x=getchar())	if(x=='-')	f=-1;
	for( ;x>='0'&&x<='9';x=getchar())	s=(s<<1)+(s<<3)+(x&15);
	return f==1?s:-s;
}

int n,k;
int ans;

signed main(){ 
	n=in,k=in;
	for(int l=1,r=0;l<=n;l=r+1){
		if(k/l==0)	break;
		r=min(k/(k/l),n);
		ans+=(k/l)*(l+r)*(r-l+1)/2;
	}
	printf("%lld\n",n*k-ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值