整除分块(超快速通/超简单超易懂/详细解析)+ 模积和(算法详解/清晰代码)(看了的人rp++++++++)

整除分块(超快速通/超简单超易懂/详细解析)+ 模积和(算法详解/清晰代码)(看了的人rp++++++++)

这是作者小蒟第一篇发出来的博客

写的不好勿怪

感谢各位读者支持吖

(博客等级不够高,没办法自定义标签,如果在已有的标签里乱选的话又有点对不起读者。所以文章可能没那么容易被检索到。看到觉得有用好看的宝麻烦口口相传推荐给身边的伙伴吖)

前言

本蒟最近做了一道题 模积和 ,被迫去学了一下整除分块。那既然学了肯定要打下总结呀,想着放在文件夹里也是落灰(大概率也不会再看了),不如发出来 造福万民 与民同乐 和大家分享。

个人认为写得还算清楚,起码能让我自己看懂。因为作者本人也非常的菜,所以难度应该还是不会很高,适用于大部分的读者。

好啦好啦不废话啦,进入正题。

前置知识

分块

有请 oi-wiki

其实,分块是一种思想,而不是一种数据结构。

分块的基本思想是,通过对原数据的适当划分,并在划分后的每一个块上预处理部分信息,从而较一般的暴力算法取得更优的时间复杂度。

分块其实蛮有用的,对于一些暴力过不了的题目,完全可以考虑分块,说不定可以卡过去😎

那么在整除分块这一知识点里面,就是把整除后值相同的分成一个块,从而将复杂度降到 O ( n ) \Omicron(\sqrt{n}) O(n )

例题引路

给出 n ,求 ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor i=1nin

我们手膜一下 n = 20 n=20 n=20 时的情况

i1234567891011121314151617181920
2010654322221111111111

看上表可知:可能会有连续的多个数 i i i ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor in 的值相等。那么这个时候就可以用到分块,因为是整除(下取整),所以也就是整除分块(说了跟说了似的

我们设每一块的左端点为 l l l,那么这一块的每个单值 k = ⌊ n i ⌋ k=\lfloor \frac{n}{i} \rfloor k=in ,右端点就是 r = ⌊ n ⌊ n i ⌋ ⌋ r=\lfloor \frac{n}{ \lfloor \frac{n}{i} \rfloor} \rfloor r=inn

那么每个分块的值就是 k ( r − l + 1 ) k(r-l+1) k(rl+1),答案累加即可。

因为 n 以内最多有 2 n 2\sqrt{n} 2n 个不同的数,所以时间复杂度就是 O ( n ) \Omicron(\sqrt{n}) O(n ) 的(省略常数项)

给下关键代码:

int s=0;
for(int l=1,r;l<=n;l=r+1)
{
	int k=n/l;
	r=n/k;
	s+=k*(r-l+1);
}

给道练习题

模积和

内容概括

给定 $ n,m $ ,求 ∑ i = 1 n ∑ j = 1 m ( n % i ) ( m % j )      i f ( i ≠ j ) \sum_{i=1}^{n} \sum_{j=1}^{m} (n\%i)(m\%j) \space \space \space \space if(i \not= j) i=1nj=1m(n%i)(m%j)    if(i=j)

其中 1 ≤ n , m ≤ 1 0 9 1 \le n,m \le 10^9 1n,m109

题目解法

显然这是 O ( n m ) \Omicron(nm) O(nm)

尝试将其优化至 O ( n ) \Omicron(n) O(n),其实也很简单,就把两个 ∑ \sum 拆开,变成1
∑ i = 1 n ( n % i ) ∑ i = 1 m ( m % i ) − ∑ i = 1 n ( n % i ) ( m % i ) \sum_{i=1}^{n} (n\%i) \sum_{i=1}^{m} (m\%i)- \sum_{i=1}^{n} (n\%i)(m\%i) i=1n(n%i)i=1m(m%i)i=1n(n%i)(m%i)

(注意,因为 i ≠ j i \not= j i=j ,所以一定不要忘记减去最后的公共部分)

但是

对于100%的数据n,m<=10^9。

所以我们需要 O ( n ) \Omicron(\sqrt{n}) O(n ) 算法

先拆柿子:我们知道 a % b a\%b a%b 等价于 a − ⌊ a b ⌋ b a-\lfloor \frac{a}{b} \rfloor b abab

所以
∑ i = 1 n ( n − ⌊ n i ⌋ i ) ∑ i = 1 m ( m − ⌊ m i ⌋ i ) − ∑ i = 1 n ( n − ⌊ n i ⌋ i ) ( m − ⌊ m i ⌋ i ) \sum_{i=1}^{n} (n-\lfloor \frac{n}{i} \rfloor i) \sum_{i=1}^{m} (m-\lfloor \frac{m}{i} \rfloor i)-\sum_{i=1}^{n} (n-\lfloor \frac{n}{i} \rfloor i) (m-\lfloor \frac{m}{i} \rfloor i) i=1n(nini)i=1m(mimi)i=1n(nini)(mimi)
再提
( n 2 − ∑ i = 1 n ⌊ n i ⌋ i ) ( m 2 − ∑ i = 1 m ⌊ m i ⌋ i ) − ∑ i = 1 n ( n − ⌊ n i ⌋ i ) ( m − ⌊ m i ⌋ i ) (n^2-\sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor i)(m^2-\sum_{i=1}^{m} \lfloor \frac{m}{i} \rfloor i)-\sum_{i=1}^{n} (n-\lfloor \frac{n}{i} \rfloor i) (m-\lfloor \frac{m}{i} \rfloor i) (n2i=1nini)(m2i=1mimi)i=1n(nini)(mimi)
f i = i 2 − ∑ j = 1 i ⌊ i j ⌋ i f_i=i^2-\sum_{j=1}^{i} \lfloor \frac{i}{j} \rfloor i fi=i2j=1ijii
f n f m − ∑ i = 1 n ( n − ⌊ n i ⌋ i ) ( m − ⌊ m i ⌋ i ) f_nf_m-\sum_{i=1}^{n} (n-\lfloor \frac{n}{i} \rfloor i) (m-\lfloor \frac{m}{i} \rfloor i) fnfmi=1n(nini)(mimi)
那么 f n f_n fn f m f_m fm 我们就可以用整除分块来做了

现在还剩 ∑ i = 1 n ( n − ⌊ n i ⌋ i ) ( m − ⌊ m i ⌋ i ) \sum_{i=1}^{n} (n-\lfloor \frac{n}{i} \rfloor i) (m-\lfloor \frac{m}{i} \rfloor i) i=1n(nini)(mimi)

我再拆
∑ i = 1 n ( n m − n ⌊ m i ⌋ i − m ⌊ m i ⌋ i + ⌊ n i ⌋ ⌊ m i ⌋ i 2 ) \sum_{i=1}^{n} (nm-n \lfloor \frac{m}{i} \rfloor i - m \lfloor \frac{m}{i} \rfloor i + \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor i^2) i=1n(nmnimimimi+inimi2)
再浅浅提一下
n 2 m − ∑ i = 1 n ( i ( n ⌊ m i ⌋ + m ⌊ n i ⌋ ) − ⌊ n i ⌋ ⌊ m i ⌋ i 2 ) n^2m-\sum_{i=1}^{n} ( i(n \lfloor \frac{m}{i} \rfloor +m \lfloor \frac{n}{i} \rfloor)- \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor i^2 ) n2mi=1n(i(nim+min⌋)inimi2)
n 2 m n^2m n2m 当然不用说啦,直接算就好了。显然 ∑ i = 1 n i ( n ⌊ m i ⌋ + m ⌊ n i ⌋ ) \sum_{i=1}^{n} i(n \lfloor \frac{m}{i} \rfloor +m \lfloor \frac{n}{i} \rfloor) i=1ni(nim+min⌋) 也是整除分块

那就只剩 ∑ i = 1 n ⌊ n i ⌋ ⌊ m i ⌋ i 2 \sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor i^2 i=1ninimi2

我们分开来看 ⌊ n i ⌋ ⌊ m i ⌋ \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor inim i 2 i^2 i2

首先看 ⌊ n i ⌋ ⌊ m i ⌋ \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor inim

这是一个二维整除分块

来,上前置知识!!!

前置芝士:二维整除分块

其实好像也没什么😂

我们以本题为例,要求 ∑ i = 1 n ⌊ n i ⌋ ⌊ m i ⌋ \sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor i=1ninim

那么对于原来每块的值 k ,我们现在要求两个 k 1 = ⌊ n i ⌋ k1=\lfloor \frac{n}{i} \rfloor k1=in k 2 = ⌊ m i ⌋ k2=\lfloor \frac{m}{i} \rfloor k2=im

那么 r = min ⁡ ( n k 1 , m k 2 ) r=\min(\frac{n}{k1},\frac{m}{k2}) r=min(k1n,k2m)

其他一毛一样(该乘乘,该干嘛干嘛)

回到题目

那么对于 ∑ i = l r i 2 \sum_{i=l}^{r} i^2 i=lri2

我们尝试使用类似前缀和的思路

∑ i = l r i 2 = ∑ i = 1 r i 2 − ∑ i = 1 l − 1 i 2 \sum_{i=l}^{r} i^2=\sum_{i=1}^{r} i^2 - \sum_{i=1}^{l-1} i^2 i=lri2=i=1ri2i=1l1i2

那么怎么求 ∑ i = 1 x i 2 \sum_{i=1}^{x} i^2 i=1xi2

我们可以知道,这等于 x ( x + 1 ) ( 2 x + 1 ) 6 \frac{x(x+1)(2x+1)}{6} 6x(x+1)(2x+1)

至于为什么

看官且看:

我们知道 ( n + 1 ) 3 − 1 3 = ∑ i = 1 n ( ( i + 1 ) 3 − i 3 ) (n+1)^3-1^3=\sum_{i=1}^{n} ((i+1)^3-i^3) (n+1)313=i=1n((i+1)3i3)

又知道 ( n + 1 ) 3 = n 3 + 3 n 2 + 3 n + 1 (n+1)^3=n^3+3n^2+3n+1 (n+1)3=n3+3n2+3n+1

把后式套入前式,得
( n + 1 ) 3 − 1 3 = ∑ i = 1 n ( 3 i 2 + 3 i + 1 ) = 3 ∑ i = 1 n i 2 + 3 ∑ i = 1 n i + ∑ i = 1 n 1 (n+1)^3-1^3=\sum_{i=1}^{n} (3i^2+3i+1)\\ =3\sum_{i=1}^{n} i^2 + 3\sum_{i=1}^{n} i + \sum_{i=1}^{n} 1 (n+1)313=i=1n(3i2+3i+1)=3i=1ni2+3i=1ni+i=1n1
s n = ∑ i = 1 n i 2 s_n=\sum_{i=1}^{n} i^2 sn=i=1ni2

则原式为
( n + 1 ) 3 − 1 3 = 3 s n + 3 ( n + 1 ) n 2 + n (n+1)^3-1^3=3s_n+3 \frac{(n+1)n}{2} +n (n+1)313=3sn+32(n+1)n+n
s n s_n sn 移至左边,其他移去右边,得
3 s n = ( n + 1 ) 2 − 1 − ( n + 1 ) n 2 − n 3s_n=(n+1)^2-1- \frac{(n+1)n}{2} - n 3sn=(n+1)212(n+1)nn
化简,得
2 ( n + 1 ) 3 − 2 − 3 ( n + 1 ) n − 2 n 6 \frac{2(n+1)^3-2-3(n+1)n-2n}{6} 62(n+1)323(n+1)n2n
展开,再整理一下,得
2 n 3 + 3 n 2 + n 6 \frac{2n^3+3n^2+n}{6} 62n3+3n2+n
提出 n n n ,得
n ( 2 n 2 + 3 n + 1 ) 6 \frac{n(2n^2+3n+1)}{6} 6n(2n2+3n+1)

记得 ÷ 6 \div6 ÷6 要用逆元,即 × 3323403 \times 3323403 ×3323403 6 6 6 % 19940417 \% 19940417 %19940417 意义下的逆元)

那么这道题就是这样了。记得要开 long long 吖

感觉讲得还是有点儿怪(tao

Talk is cheap ,show you the code.

#include<cstdio>
#define ll long long int 
#define Md 19940417
#define Inv6 3323403
using namespace std;
ll min1(ll x,ll y)
{
	return x<y?x:y;
}
void swp(ll &x,ll &y)
{
	ll t=x;
	x=y;
	y=t;
}
ll sm(ll l,ll r)
{
	return ((l+r)*(r-l+1))/2%Md;
}
ll f(ll x)
{
	ll l=1,r;
	ll s=(x*x)%Md;
	while(l<=x)
	{
		ll k=x/l;
		r=x/k;
		s=(s-(sm(l,r)*k)%Md+Md)%Md;
		l=r+1;
	}
	return s;
}
ll g(ll x)
{
	return (((x*(x+1))%Md*(2*x+1))%Md*Inv6)%Md;
}
int main()
{
	ll n,m;
	scanf("%lld%lld",&n,&m);
	if(n>m)
	{
		swp(n,m);
	}
	ll ans=((f(n)*f(m))%Md-((n*n)%Md*m)%Md+Md)%Md;
	ll l=1,r;
	while(l<=n)
	{
		ll k1=n/l,k2=m/l;
		r=min1(n/k1,m/k2);
		ans=((ans+(sm(l,r)*((n*k2)%Md+(m*k1)%Md)%Md))%Md-((g(r)-g(l-1)+Md)%Md*k1%Md*k2)%Md+Md)%Md;
		l=r+1;
	}
	printf("%lld",ans);
	return 0;
}

如有错误或遗漏,烦请各位评论区指正。

码字不易(公式打得手软),还请大家多多支持,可以的话点赞关注加收藏哦

谢谢啦🧡


  1. 这里默认 n ≤ m n \le m nm ↩︎

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值