数论学习笔记(整除分块+积性函数+筛子)
数论分块
看之前没有讲过先提一下,后面会用到。
问题一般是已知一个数 \(n\) 和两个函数 \(f,g\),求 \(\sum_{x=1}^n f(i)g(\left\lfloor\frac{n}{x}\right\rfloor)\)。
这个东西可以考虑 \(\left\lfloor\frac{n}{x}\right\rfloor\) 有多少种不同的取值,假设 \(\left\lfloor\frac{n}{x}\right\rfloor=y\)。
考虑根号分治,对于 \(x<\sqrt{n}\),取值显然最多 \(\sqrt{n}\) 种,对于 \(x>\sqrt{n}\),\(y\le \sqrt{n}\),最多也只有 \(\sqrt{n}\),所以总取值数是 \(2\sqrt{n}\) 级别的。
知道这个东西怎么求出对于一个取值 \(y\),满足这个取值的区间 \([l,r]\) 呢?
一开始学这个东西的时候我是直接整体二分的,这个应该大家都会。
考虑对于一个 \(l\),怎么去求 \(r\)。
设 \(k=\left\lfloor\frac{n}{l}\right\rfloor\),\(k\le \frac{n}{l}\)。
因为 \(k=\max l\),所以 \(r=\left\lfloor\frac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor\)。
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
//work(l,r)
}
时间复杂度显然 \(O(\sqrt{n})\)。
先来个例题。P2261
已知 \(n,k\),求
\[\sum_{i=1}^n k\bmod i \]\(n,k\le 10^9\)。
做法比较简单,就是考虑 \(k\bmod i=k-i\left\lfloor\frac{k}{i}\right\rfloor\),前面部分是 \(nk\),后面部分直接用板子即可。code(用整体二分写的)。
P2260 和这个题做法几乎一样。
若 \(x\) 分解质因数结果为 \(x=p_1^{k_1}p_2^{k_2}\cdots p_n^{k_n}\),令\(f(x)=(k_1+1)(k_2+1)\cdots (k_n+1)\),求 \(\sum_{i=l}^rf(i)\) 对 \(998\,244\,353\) 取模的结果。
\(l,r\le 10^{14}\)
\(f(x)\) 显然就是 \(x\) 的因数个数,然后这个区间和可以变成前缀和的形式,也就是求 \(\sum_{i=1}^n f(i)\),直接考虑一个数作为因数对答案的贡献, \(i\) 对答案的贡献显然就是 \(\left\lfloor\frac{n}{i}\right\rfloor\),然后直接整除分块即可。
积性函数
就是一类函数的定义满足对于任意互质的数 \(a,b\),\(f(ab)=f(a)f(b)\)。
对于任意数 \(a,b\) 都满足 \(f(ab)=f(a)f(b)\) 的函数称为完全积性函数。
容易发现 \(f(1)=1\)。
所以也可以写成分段形式:
也就是先处理出质数幂次的地方的取值,然后就是相乘的形式,这个可以直接用线性筛。
下面是几个常见的积性函数。
- \(I\),\(f(x)=1\)
- \(F_k\),\(f(x)=x^k\)。
- 欧拉函数
- 莫比乌斯函数
- \(d(x)\),表示 \(x\) 的约数个数,还有约数之和。
一个等式:
这个证明起来也比较简单,考虑对于每个质数,在 \(x,y\) 中选到的总方案为它的指数加一,这个等式在P3327中用到。
欧拉函数
\(\varphi(x)\) 表示 \([1,x]\) 有多少个数与 \(x\) 互质,这就是欧拉函数。
首先来证明这个是积性函数。
考虑对于两个数 \(a,b\),假设 \(S_a\) 为与 \(a\) 互质的数的集合,\(S_b\) 表示与 \(b\) 互质的数的集合。
那么任意取 \(x\in S_a,y\in S_b\),可以构造出 \(xb+ya\) 与 \(ab\) 互质。
为什么呢?首先 \(\gcd(a,x)=1,\gcd(b,x)=1\to \gcd(ab,x)=1\)。
那么 \(\gcd(xb+ya,a)=\gcd(xb,a)=1\),同理与 \(b\) 也互质。
再证明已经取完了所有的数,由于 \(a,b\) 互质,所以 \(xb+ya\) 在 \(\bmod ab\) 意义下可以取遍所有数,那么如果 \(x\notin S_a\),那么就不互质了,同理 \(y\) 也是。
所以 \(\varphi(ab)=\varphi(a)\varphi(b)\)。
然后来考虑咋求。
积性函数都是先考虑质数幂次处的取值。
\(f(p^k)=p^k-p^{k-1}=p^{k-1}(p-1)\)。
那么 \(f(x)=\prod p_k^{k_i-1}(p_k-1)=x\prod\frac{p_i-1}{p_i}\)。
暴力做就是 \(\sqrt{x}\) 的。
int euler_phi(int n) {// 贺的
int ans = n;
for (int i = 2; i * i <= m; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
然后考虑筛,也很简单,考虑如果当前是之前没有出现过的就直接乘上其欧拉函数值,否则之前出现过,容易发现 \(\varphi(p^k)=\varphi(p^{k-1})p\) 所以直接乘上即可,时间复杂度 \(O(n)\)。
void work(){
ou[1]=1;
for(int i=2;i<=N;i++){
if(!b[i])p[++cnt]=i,ou[i]=i-1;
for(int j=1;j<=cnt&&p[j]*i<=N;j++){
b[p[j]*i]=1;
if(i%p[j]!=0)ou[i*p[j]]=ou[i]*(p[j]-1);
if(i%p[j]==0){
ou[i*p[j]]=ou[i]*p[j];
break;
}
}
}
}
下面来个等式。
这个东西考虑证明,把 \(1\le x\le n\) 的数 \(x\) 拆成 \(\gcd(n,x)\times \frac{x}{\gcd(n,x)}\),\(\frac{x}{\gcd(x,n)}\) 显然与 \(\frac{n}{\gcd(x,n)}\) 互质,对于 \(d|n\),满足条件的个数就是 \(\varphi(\frac{n}{d})\),换成枚举 \(\frac{n}{d}\) 就可以得到上面的式子了。
来个例题
有一个 \(n\times n\) 的矩阵,有一个人站在 \((1,1)\),问他可以看到多少人。
\(n\le 40000\)。
本质上就是这个东西
考虑取一半,变成
算出来乘个 \(2\) 即可。
欧拉定理
对于 \(\gcd(a,p)=1\),满足
考虑 \(a^x\) 在 \(\bmod p\) 意义下的取值,首先肯定是 \([0,p-1]\),假设就是 \([1,p]\) 好了。
然后这个取值一定是与 \(p\) 互质,如果不互质,也就是 \(\gcd(a^x,p)>1\),那么显然 \(\gcd(a,p)>1\) 矛盾。
然后可以发现,如果一个数 \(b\) 与 \(p\) 互质,那么 \(ab\) 也和 \(p\) 互质。
所以开始构造一个序列 \(r_1,r_2,...,r_{\varphi(p)}\),表示所有与 \(p\) 互质的数。
考虑序列 \(ar_1,ar_2...ar_{\varphi(p)}\) ,这个序列在模 \(p\) 意义下必然两两不同,可以用反证法简单证明,然后又可以发现这个序列所有数与 \(p\) 互质。
所以
所以可以证明 \(a^{\varphi(p)} \bmod p=1\)。
如果 \(p\) 是质数,就可以证明费马小定理,\(a^{p-1}\bmod p=1\)。
莫比乌斯函数、反演
先进行一个引入。
之前讲过二项式反演,反演的本质就是两个序列 \(f,g\) 满足一定关系,可以用 \(g\) 表示 \(f\),然后反演就是把 \(f\) 来表示 \(g\),二项式反演很有用是因为可以 \(O(n\log n)\) 的多项式卷积加速,否则很多反演都可以用 \(O(n^2)\) 直接做。
比如下面的反演是这样一个东西:
考虑怎么去反演,暴力 \(O(n^2)\) 的自然不用说了。
此时就可以用莫比乌斯函数写成更加优美的形式了(虽然还是 \(O(n^2)\))
本质上这里莫比乌斯函数就是当一个容斥系数。
给出 \(\mu\) 的定义:
再去考虑上面那个容斥,可以发现贡献算的就是多出来的质因数,进行一个多维容斥。
莫比乌斯函数也可以直接用线性筛算。
考虑 \(\mu\) 与 \(\varphi\) 之间的关系。
首先可以得到 \(n=\sum_{d|n}\varphi (d)\),这个把所有 \(\le n\) 的数按照 \(\gcd(i,n)\) 分组即可。
直接反演:
求
两边同取 \(\ln\)。
反演回来。
大力分类讨论,可以得到:
这不是积性函数,但是可以线性做。
来个用这个反演的题
定义一个数 \(x\) 是好的,当且仅当 \(x\) 不含有一个平方因子,求第 \(n\) 个好的数。
\(n\le 10^{10}\)。
第一步显然直接二分即可,然后相当于求前缀好的数的个数,这个平方因子定义显然就可以变成 \(\mu^2(x)\),等于一就表示是好的数,然后相当于求 \(\sum_{i=1}^n \mu^2(i)\),可以直接上杜教筛或者 min-25 筛,但是常数巨大而且没必要。
考虑设 \(f_i\) 表示是 \(i^2\) 倍数的数的个数,\(g_i\) 表示 \(i^2\) 是 \(x\) 的最大平方因子的 \(x\) 的个数,那么要求的就是 \(g_1\)。
那么显然
那么 \(d\) 只需要枚举到 \(\sqrt{n}\) 即可,因此总复杂度 \(O(\sqrt{n}\log n)\)。
有许多数论题目与 \(\gcd\) 的求和有关,可以用莫比乌斯反演做,当然不是上面 \(O(n^2)\) 的形式。
首先需要证明,\(\sum_{d|n}\mu(d)=[n==1]\)。
\(n=1\) 显然,否则考虑每个质数的贡献,就相当于是一个二进制数 \(2^n-1\),每一位选或者不选,总贡献就是 \(0\)。
莫反的基本形式就是
然后可以把 \(d\) 的枚举放到 \(a,b\) 前面,这样就需要满足 \(d|a,d|b\),这样就可以反演了。
然后来几个例子,默认 \(n<m\):
已知 \(n,m\),求
\[\sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j)==1] \]
然后整除分块、暴力即可。
已知 \(n,m\),求
\[\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j) \]
已知 \(n,m\),求
\[\sum_{i=1}^n \sum_{j=1}^m f(\gcd(i,j)) \]\(f(x)\) 是一个函数。
此时枚举 \(\gcd(i,j)\) 即可。
此时令 \(T=dp\)。
\(\sum_{d|T}\mu(d)f(\frac{T}{d})\) 部分可以直接狄利克雷卷积预处理,然后就可以整除分块了。
莫反题很多,但方法都差不多,试试看!
P5518
阴间题。
\(type=1\)。
这个时候把问题分成两半。
另一半就是在这个的基础上乘一个 \(\prod_{i=1}^A \prod_{j=1}^B ij\)。
\(type=1\)。
令 \(g(x)=\frac{x(x+1)}{2}\)
其实和 \(type=0\) 比较类似。
\(type=2\)。
这里的 \(Q\) 就是 \(type=0\) 的情况。
大力做,是 \(O(Tn\log^2 n)\) 的,寄了,可以对每一个都整除分块一下。
复杂度不知道,反正是最优解第三。
按素数考虑
别莫反魔怔了。
在 \([1,n]\) 中选出若干个数,使得全部 \(\gcd=1\),求方案数。
直接枚举 \(\gcd\) 即可。
直接做 \(O(n)\),可以整除分块,复杂度 \(O(\sqrt{n}\log n)\)。
那如果要求 \(\gcd=1,\text{lcm}=L\) 呢?loj2257
这个就相对阴间了。
莫反:
容斥一下,分别算 \(\gcd>1,\text{lcm}<L,\gcd>1 并且 \text{lcm}<L\) 的方案数即可。
前两个可以直接 \(O(n\log n)\) 做,最后那个可以枚举 \(\text{lcm}\) 然后做,复杂度 \(O(n\log^2 n)\),很寻,而且不能修改。
考虑把 \(f(i)=\frac{L}{i}\),那么 \(\text{lcm}=L\) 等价于 \(f(i)\) 的 \(\gcd=1\)。
因此把每个数看成二元组 \((i,\frac{L}{i})\),相当于选出若干个使得两个都 \(\gcd =1\)。
大概是这样。
还是很难算,但是会发现含有平方因子的东西 \(\mu=0\) 没有贡献,因此只需要考虑若干个质数乘起来即可。
每个质数只有选或者不选,因此可以状压,就变成了选若干个数与起来等于 \(0\)。
直接 FWT,复杂度 \(O(2^{2w(L)})\),\(w(L)\) 表示 \(L\) 质因子个数。
带修的话就考虑修改的每个点对 \(0\) 点的贡献是 \(1\) 或者 \(-1\),直接算上即可,这样复杂度 \(O(w(L)2^{2w(L)})\)。
丢番图
这个东西洛谷评绿,但是总是推不出来。
loj6482 \(\color{Gold}\bigstar\)
设 \(x=a-c,y=b-c\),那么 \(\gcd(a,b,c)=\gcd(x,y,c)=1\)。
因为 \(x,y\) 中的每个质因子 \(c\) 中都有,所以只需要满足 \(\gcd(x,y)=1\),那么可以发现 \(x,y\) 都需要是完全平方数。
写个暴力式子,直接去枚举 \(c,\sqrt{x}\)。
后面两个条件比较阴间,换到前面来。
杜教筛
这个算法用来算积性函数前缀和。也就是
单次询问复杂度为 \(O(n^{\frac{2}{3}})\)。
考虑构造一个函数 \(g\),满足 \(g\) 的前缀和很好求,并且 \(f\) 和 \(g\) 的狄利克雷卷积出的函数 \(h\) 的很好求。
因此可以得到
\(\sum_{i=1}^n h(i)\) 很好求,右边的东西可以直接整除分块,然后一直递归下去,记忆化一下复杂度是 \(O(n^{\frac{3}{4}})\)。
用线性筛筛出前 \(n^{\frac{2}{3}}\) 的答案,复杂度就是 \(O(n^{\frac{2}{3}})\)。
主要的难度在于构造 \(g\),所以杜教筛的使用范围还是比较小的。
已知 \(n\),求下面两个式子的值
\[\sum_{i=1}^n \mu(i)\\ \sum_{i=1}^n \varphi(i) \]\(n\le 2^{31}\)。
这两个构造比较简单,都是 \(I,f(x)=1\) 即可。这个东西前缀和很好求,而且卷积后的东西:
前缀和都很简单,那么直接求即可。
放一个求 \(\varphi\) 的代码:
ll sum_phi(int n){
if(n<=1e6)return phi[n];
if(mp_phi.count(n))return mp_phi[n];
ll ans=1ll*n*(1ll*n+1)/2,r;
for(ll l=2;l<=n;l=r+1){
r=n/(n/l);
ans-=1ll*(r-l+1)*sum_phi(n/l);
}
return mp_phi[n]=ans;
}
给定两个整数 \(n,p\),求
\[\sum_{i=1}^n \sum_{j=1}^n ij\gcd(i,j) \pmod p \]\(n\le 10^{10}\)
先和仪仗队差不多,直接推式子。
后面那个部分可以直接整除分块然后 \(O(1)\) 算,问题变成求 \(\sum_{d=1}^n \varphi(d)d^2\)。
这个东西配一个 \(g(x)=x^2\),可以发现 \((f * g)(n)=n^3\),所以可以直接杜教筛求了。
这个整除分块+杜教筛的复杂度应该是 \(O(n^{\frac{2}{3}})\) 的。
代码可以看 froggy 的题解,我写的代码疯狂 WA,调不出来。
min 25 筛
这个东西比杜教筛跟牛一点,可以处理几乎所有积性函数了。
依然考虑
这里需要满足 \(f(x)\) 是积性函数并且在 \(p\) 为质数时 \(f(p)\) 时一个关于 \(p\) 的多项式,并且 \(f(p^c)\) 比较好求。
主要的思想是把这个和拆成 \(1\),质数,合数这三个部分。
part 1 预处理
下面约定 \(p_i\) 表示第 \(i\) 个质数。
令一个完全积性函数 \(f'(x)\) 在质数位置取值与 \(f(x)\) 相同,如果这个比较难构造,可以变成多个函数相加的形式。
先考虑这样一个 dp,\(g(n,k)\) 表示对于所有 \(2\le i\le n\),满足 \(i\) 是质数或者 \(i\) 的最小质因数 \(\ge p_k\),\(f'(i)\) 的和。
考虑这个东西去转移。
如果 \(p_k^2>n\),那么 \(p_k\) 去不掉任何一个合数,因此没有贡献,所以 \(g(n,k)=g(n,k-1)\)。
否则,考虑 \(p_k\) 会把那些给删掉,容易发现就是满足最小质因数为 \(p_k\) 的合数。
由于是完全积性函数,所以可以把一个 \(f'(p_k)\) 拿出来,这样就得到了 \(f'(p_k)g(\left\lfloor\frac{n}{p_k}\right\rfloor,k-1)\),
然后考虑有一些东西会被多算,也就是含有小于 \(p_k\) 的质数,减掉即可,整理一下就是
此处由于 \(g(n,k)\) 是不算 \(1\) 的,所以 \(f'(p_k)\) 恰好被保留了下来。
注意这个地方是拿完全积性函数 \(f'(x)\) 算的,网上一些博客拿 \(f(x)\) 算显然是错的。
part 2 算答案
考虑类似的,设 \(G(n,k)\) 表示对于所有 \(2\le i\le n\),满足 \(i\) 的最小质因数 \(\ge p_k\),\(f(i)\) 的和。
考虑这个东西怎么算,把它拆成质数,合数两个个部分。
质数部分的话,\(f(p)=f'(p)\),所以可以用 \(g\) 来算,也就是 \(g(n,|P|)\),\(|P|\) 是 \(\le n\) 质数总个数,然后由于前 \(k\) 个质数是没有贡献的,所以再减去 \(g(p_k,k)\)。
合数部分,考虑枚举质数以及枚举其次数即可,这部分贡献是 \(\sum_{j>k} \sum_{e\ge 1} f(p_j^e)(G(\left\lfloor\frac{n}{p_j^e}\right\rfloor,j)+[p>1])\)。
后面要加 \(p>1\) 是因为 \(G\) 不算 \(1\) 的贡献,要加上 \(f(p_j^e)\) 的贡献,去掉 \(e=1\) 是因为前面质数部分已经算过了。
所以可以得到
最后的答案就是 \(f(1)+G(n,0)\)。
part 3 具体实现
先去求 \(g(n,|P|)\),可以发现第一维都是 \(\left\lfloor\frac{n}{p_k}\right\rfloor\) 的形式,所以第一维出现的数只有 \(\sqrt{n}\) 个,那么先预处理出所有 \(g(x,0)\),然后一层一层地进行递推即可。
注意计算初值时,一定要先把 \(f'(1)\) 给去掉。
然后直接照着上面的式子去算 \(G\) 即可,复杂度为 \(O(\frac{n^{\frac{3}{4}}}{\log n})\),不过也有人说是 \(O(n^{1-ϵ})\)。
求 \([1,n]\) 素数个数。
\(n\le 10^{11}\)
显然就相当于是 \(f'(x)=1\),然后直接求 \(g(n,|P|)\) 即可。
这个东西直接用上面的东西 dp 一遍即可。
贴个核心代码:
int get_id(int x){
if(x<sq)return mp1[x];
return mp2[n/x];
}
signed main(){
scanf("%lld",&n);sq=sqrt(n);
pre(sq);
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
w[++m]=n/l,g[m]=w[m]-1;
if(n/l<sq)mp1[n/l]=m;else mp2[r]=m;// 注意这里 mp2 是 r,调了很久,是因为存的值是 n/l,后面调用是 n/x,也就是 n/(n/l),所以直接就是 r
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<=m&&p[i]*p[i]<=w[j];j++)
g[j]-=g[get_id(w[j]/p[i])]-s[p[i]-1];//这里 s[i] 表示 [1,i] 的素数个数
printf("%lld",g[1]);
}
已知积性函数 \(f(x)\) ,满足 \(f(p^k)=p^k(p^k-1)\)。
求
\[\sum_{i=1}^n f(i) \]\(n\le 10^{10}\),答案对 \(10^9+7\) 取模。
先考虑求质数地方的,那么就是 \(f(p)=p(p-1)\),拆成完全积性函数,就是 \(f(p)=(p^2)-p\),对两个分别处理然后相减即可,然后再去求一下答案。
求答案部分代码:
int G(int n,int k){
if(p[k]>n)return 0;
int ans=(g[get_id(n)]+s1[p[k]]-s2[p[k]]+mod)%mod;
for(int i=k+1;i<=cnt&&p[i]*p[i]<=n;i++)
for(int t=p[i];t<=n;t*=p[i]){
int T=t%mod;
ans=(ans+T*(T-1)%mod*(G(n/t,i)+(t!=p[i]))%mod)%mod;
}
return ans;
}