整除分块
整除分块是莫比乌斯反演的常见优化之一
整除分块
从一维入手:
求\(\sum\limits_{i=1}^n\lfloor\frac{n}{i}\rfloor\pmod{1000003}\)
我们发现\(⌊\frac{n}i⌋\)有以下性质:
- \(⌊\frac{n}i⌋\)最多只有\(2\sqrt{n}\)种取值
证明:对于\(i≤\sqrt{n}\), 只有\(\sqrt{n}\)种,对于\(i>\sqrt{n}\),\(⌊\frac{n}i⌋<\sqrt{n}\),也只有 \(\sqrt{n}\) 种取值,共计 \(2\sqrt{n}\) 种
- 设 \(⌊\frac{n}{i'}⌋\) 与 \(⌊\frac{n}i⌋\) 相等,则\(i'\)的最大值为 \(⌊\frac{n}{⌊\frac{n}i⌋} ⌋\)
证明:
设 \(⌊\frac{n}i⌋=k\) ,于是可以写成 \(ki+p=n,1≤p<i\)的形式,若\(⌊\frac{n}{i+d}⌋=k\) ,于是有\(k(i+d)+p'=n\)可以得到\(p'=p−kd\),则\(d\)能取的最大值为\(⌊\frac{p}k⌋\) ,于是 :
\(i′\\=i+d_{\max}=i+⌊\frac{p}k⌋\\=i+⌊\frac{n\mod i}{⌊\frac{n}i⌋}⌋\\=i+⌊\frac{n−⌊\frac{n}i⌋i}{⌊\frac{n}i⌋}⌋\\=⌊i+\frac{n−⌊\frac{n}i⌋i}{⌊\frac{n}i⌋}⌋\\=⌊\frac{n}{⌊\frac{n}i⌋} ⌋\)
设两个指针 \(l\) 和 \(r\) , \(l\) 的初始值为 \(1\) ,每次令 \(r=⌊\frac{n}{⌊\frac{n}l⌋}⌋\) ,将 \((r−l+1)⋅⌊\frac{n}{l}⌋\) 累加至答案中 ,再令 \(l=r+1\),不断循环。
这样就把\(⌊\frac{n}{x}⌋=⌊\frac{n}{l}⌋\)的所有\(x\)都加上了。
由于 \(⌊\frac{n}l⌋\) 只有 \(2\sqrt{n}\) 种取值 ,且单调递减,则最多只有\(2\sqrt{n}\)个取值不同的段,时间复杂度为 \(O(\sqrt{n})\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000003;
ll n,m,l,r;
int ans;
int main() {
scanf("%lld",&n);
for(l=1; l<=n; l=r+1) {
r=n/(n/l);
ans=(ans+((n/l)%mod)*(r-l+1))%mod;
}
printf("%d\n",ans);
return 0;
}
再看二维:
求\(\sum\limits_{i=1}^n\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor\pmod{1000003}\)
和一维差不多,就这样:
然后,设两个指针 \(l\) 和 \(r\) , \(l\) 的初始值为 \(1\) ,每次令 \(r=\min(⌊\frac{n}{⌊\frac{n}l⌋}⌋,⌊\frac{m}{⌊\frac{m}l⌋}⌋)\) ,将 \((r−l+1)⋅⌊\frac{n}{l}⌋⌊\frac{m}{l}⌋\) 累加至答案中 ,再令 \(l=r+1\),不断循环。
这样就把\(⌊\frac{n}{x}⌋⌊\frac{m}{x}⌋=⌊\frac{n}{l}⌋⌊\frac{m}{l}⌋\)的所有\(x\)都加上了,时间复杂度为 \(O(\sqrt{n}+\sqrt{m})\)。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000003;
ll n,m,l,r;
int ans;
int main() {
scanf("%lld%lld",&n,&m);
if(n>m)swap(n,m);
for(l=1; l<=n; l=r+1) {
r=min(n/(n/l),m/(m/l));
ans=(ans+((n/l)%mod)*((m/l)%mod)%mod*(r-l+1))%mod;
}
printf("%d\n",ans);
return 0;
}
例题
我们知道,\(k\mod i=k-i\lfloor\dfrac{k}{i}\rfloor\)
代回原式就是\(\sum\limits_{i=1}^nk-i\lfloor\dfrac{k}{i}\rfloor=nk-\sum\limits_{i=1}^ni\lfloor\dfrac{k}{i}\rfloor\)
后面的部分和整除分块差不多,假设当前整除分块的块是区间\([l,r]\),
则\(\sum\limits_{i=l}^ri\lfloor\dfrac{k}{i}\rfloor=\lfloor\dfrac{k}{l}\rfloor\sum\limits_{i=l}^ri=\lfloor\dfrac{k}{l}\rfloor\dfrac{(r-l+1)(l+r)}{2}\)
后面的一个等号靠等差数列求和公式可得。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
ll ans;
int main() {
scanf("%d%d",&n,&k);
for(int l=1,r,p=min(n,k); l<=p; l=r+1) {
r=min(k/(k/l),n);
ans+=(r-l+1ll)*(l+r)/2*(k/l);
}
printf("%lld\n",n*1ll*k-ans);
return 0;
}
先加上\(i,j\)相等的部分
\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m(n\mod i)(m\mod j)-\sum\limits_{i=1}^n(n\mod i)(m\mod j)\)
先看减号前的部分
提出\((n\mod i)\)
\(\sum\limits_{i=1}^n(n\mod i)\sum\limits_{j=1}^m(m\mod j)\)
设\(f(n)=\sum\limits_{i=1}^n(n\mod i)\),则上式\(=f(n)f(m)\)
再看减号后
\(\sum\limits_{i=1}^n(n\mod i)(m\mod j)\)
\(=\sum\limits_{i=1}^n(n-i\lfloor\dfrac{n}{i}\rfloor)(m-i\lfloor\dfrac{m}{i}\rfloor)\)
拆括号!
\(=\sum\limits_{i=1}^n(nm-ni\lfloor\dfrac{m}{i}\rfloor-mi\lfloor\dfrac{n}{i}\rfloor+i^2\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{m}{i}\rfloor)\)
\(=n^2m-\sum\limits_{i=1}^nni\lfloor\dfrac{m}{i}\rfloor-\sum\limits_{i=1}^nmi\lfloor\dfrac{n}{i}\rfloor+\sum\limits_{i=1}^ni^2\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{m}{i}\rfloor\)
设\(g(n,k)=\sum\limits_{i=1}^ni\lfloor\dfrac{k}{i}\rfloor,A=\sum\limits_{i=1}^ni^2\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{m}{i}\rfloor\)
则上式等于\(n^2m-ng(n,m)-mg(n,n)+A\)
代回原式得\(f(n)f(m)-n^2m+ng(n,m)+mg(n,n)-A\)
\(g\)也可以像上题一样整除分块,现在只要求出\(A\)即可。
我们也是可以用整除分块,设当前分的块为\([l,r]\),则这块的和为:
\(\sum\limits_{i=l}^ri^2\lfloor\dfrac{n}{i}\rfloor\lfloor\dfrac{m}{i}\rfloor\)
\(=\lfloor\dfrac{n}{l}\rfloor\lfloor\dfrac{m}{l}\rfloor\sum\limits_{i=l}^ri^2\)
我们设\(S(n)=\sum\limits_{i=1}^ni^2=\dfrac{n(n+1)(2n+1)}{6}\),
则\(\sum\limits_{i=l}^ri^2=S(r)-S(l-1)\)。这样就可以整除分块了。
可以发现\(f(n)=n^2-g(n,n)\)(上题的结论),代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=19940417,inv6=3323403,inv2=9970209;
typedef long long ll;
int n,m;
int g(int n,int m) {
ll ans=0;
for(int l=1,r; l<=m; l=r+1) {
r=min(n/(n/l),m);
ans+=(r-l+1ll)*(l+r)%mod*inv2%mod*(n/l)%mod;
ans%=mod;
}
return ans;
}
int f(int n) {
return (n*1ll*n-g(n,n))%mod;
}
int S(int n) {
return n*(n+1ll)%mod*(n+1ll+n)%mod*inv6%mod;
}
ll ans;
int main() {
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
ans=1ll*f(n)*f(m)%mod;
ans=(ans+1ll*n*g(m,n)+1ll*m*g(n,n))%mod;
ans=(ans-1ll*n*n%mod*m)%mod;
for(int l=1,r; l<=n; l=r+1) {
r=min(n/(n/l),m/(m/l));
ans=(ans-1ll*(n/l)*(m/l)%mod*(S(r)-S(l-1)))%mod;
}
printf("%d\n",(ans%mod+mod)%mod);
return 0;
}