整除分块

整除分块是莫比乌斯反演的常见优化之一

整除分块

从一维入手:

\(\sum\limits_{i=1}^n\lfloor\frac{n}{i}\rfloor\pmod{1000003}\)

我们发现\(⌊\frac{n}i⌋\)有以下性质:

  1. \(⌊\frac{n}i⌋\)最多只有\(2\sqrt{n}\)种取值

证明:对于\(i≤\sqrt{n}\), 只有\(\sqrt{n}\)种,对于\(i>\sqrt{n}\),\(⌊\frac{n}i⌋<\sqrt{n}\),也只有 \(\sqrt{n}\) 种取值,共计 \(2\sqrt{n}\)

  1. \(⌊\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;
}

例题

P2261 [CQOI2007]余数求和

我们知道,\(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;
}

P2260 [清华集训2012]模积和

先加上\(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;
}
posted @ 2021-03-11 13:03  mod998244353  阅读(113)  评论(0编辑  收藏  举报
Live2D