[专题总结]进阶数论
狄利克雷相关
狄利克雷卷积
\((a*b)[x]=\sum_{i \times j} a[i]b[j]\) , 本质上就是乘法的卷积。
基本的数论函数
- \(I(n)\) ,永远等于1
- \(e(n)\) , \([n==1]\)
- \(id^k(n)\) ,\(n^k\)
- \(\mu(n)\) , 莫比乌斯函数, \(n\) 内有平方因子则是 \(0\), 否则是 \((-1)^{因子个数}\)
- \(\varphi(n)\) , 欧拉函数, \(<n\) 的与 \(n\) 互质的数的个数。
- \(\sigma_k(n)\) ,除数函数,所有约数 \(k\) 次方的和,特别的, \(\sigma_0(n)\) 是 \(n\) 的约数个数, \(\sigma_1(n)\) 是约数和。
基本结论
- \(I*\varphi=id\)
- \(\sigma_0(n)=I*I\)
- \(\sigma_1(n)=I*id\)
- \(e=\mu*I\)
狄利克雷前后缀和
我们在做狄利克雷卷积的时候,一般都是暴力枚举倍数去卷积。
和子集反演暴力枚举子集并不优一样,狄利克雷卷积也有更快的写法
假如我们要把一个函数卷上 \(I\), 使得 \(f'(i)=\sum_{d|i}f(d)\)
我们可以把每个因数当成一维,在这一维上前缀和。
先筛出来所有的质数,枚举即可。
int pr[N], top;
for(R i = 1; i <= top; i++)
for(R j = 1; j * pr[i] <= n; j++)
F[j * pr[i]] += F[j];
把一个函数卷上 \(\mu\), 本质上就是在每一维差分,写高维前缀差分即可, \(f‘(i)=\sum_{d|i} \mu(d) f(i/d)\)
int pr[N], top;
for(R i = 1; i <= top; i++)
for(R j = n / pr[i]; j; j--)
F[j * pr[i]] -= F[j];
复杂度是 \(\sum_{prime <n} n /prime = O(n\log\log(n))\)
莫比乌斯反演
感觉也没什么好写的了,主要就是要注意不要只会用 \([n==1]=\sum_{d|n} \mu(d)\) 。
要学会像容斥一样用 \(\mu\) , 他只是一个特殊的容斥系数,计算单独一个的答案不好算的时候,可以考虑计算他所有倍数的答案或者他所有约数的答案。
线性筛
线性筛可以在线性时间内筛出所有积性函数 \(\leq n\) 的所有点值。
不过需要满足在质数的次幂的点值可以快速得到。
然后我们维护一个 \(low_i\) 代表 \(i\) 的最小质因子的对应次幂的值(也就是把 \(i\) 唯一分解成 \(\prod p_i^{e_i}, p_1^{e_1}\) 的值)。
如果是质数或者这两个数互质,那么直接平凡转移。
如果不互质,那么我们把他所有的最小质因子提出来。
如果 \(low_i=i\), 那么直接套用质数次幂的点值。
否则,提出所有最小质因子之后, \(low_i\) 与 \(i/low_i\) 互质,可以直接套用积性去转移。
杜教筛
如果你有一个 \(A=B*C\), 那么,你只要可以支持快速查询 \(A,B,C\) 中任意两个的块筛值,那么也可以求出另一个的块筛值。
一个函数 \(F\) 关于 \(n\) 的块筛值定义为所有的 \(S_F(\lfloor n / i \rfloor)\) , 其中 \(S_F\) 代表 \(F\) 的前缀和,容易知道这样的点值只有 \(\sqrt n\) 个。
由于块筛中包含 \(n\), 所以常用来求一个函数 \(n\) 的前缀和。
先来看一个简单的,知道 \(B,C\) 求 \(A\) 。
\(\displaystyle S_A(n)=\sum_{i=1}^n \sum_{j|i} B(j)C(i/j)=\sum_{j=1}^nB(j)\sum_{j|i}C(i/j)=\sum_{j=1}^nB(j)S_C(n/j)\)
直接一发整出分块带走,复杂度 \(O(\sqrt n)\) .
再来知道 \(A,B\) 求 \(C\)
\(\displaystyle S_A(n)=\sum_{i=1}^n \sum_{j|i} B(j)C(i/j)=\sum_{j=1}^nB(j)\sum_{j|i}C(i/j)=\sum_{j=1}^nB(j)S_C(n/j)=B(1)S_C(n)+\sum_{j=2}^nB(j)S_C(n/j)\)
移项,由于是积性函数,所以 \(B(1)=1\), 得到 \(\displaystyle S_C(n)=S_A(n)-\sum_{j=2}^n B(j)S_C(n/j)\)
整除分块求前面的,需要的继续递归,在每个状态扩展 \(\sqrt n\) , 复杂度 \(\sum_{i=1}^{\sqrt n} (\sqrt i+ \sqrt{n/i})=n^{\frac{3}{4}}\)
计算复杂度的小技巧: \(\displaystyle \sum_{i=1}^{n^r} i^p=n^{r(p+1)}\), 来源于积分。
\(n^{\frac{3}{4}}\) 并不够优秀,我们可以设置一个阈值 \(B\), \(<B\) 的暴力线性筛, \(>B\) 的再使用杜教筛。
这样,复杂度是 $\displaystyle B+\sum_{i=1}^{n/B} \sqrt {n/i} $ ,取 \(B=n^{2/3}\) 可得复杂度为 \(O(n^{2/3})\) .
min_25筛
基础知识
适用范围:
- 求一个函数前缀和。
- 这个函数可以在质数位置写成若干个完全积性函数的和。
- 在质数次幂的值可以快速得到。
- 积性?貌似不必局限,积性肯定能做
第一步,把答案按质数和非质数分开去求。
也就是现在有了质数次幂地方的值,就可以把问题转化成求质数处的点值和最小质因子大于一个质数的点值。
第二步, 设计一个求质数位置值的 dp。
其中,由于质数位置可以拆成若干个完全积性函数,假设 \(t(i)\) 是其中的一个,我们拆成若干个加起来就可以得到质数位置的点值。
\(P_j\) 代表第 \(j\) 个质数, \(minp(i)\) 是 \(i\) 的最小质因子,\(|P|\) 是范围内所有质数, 可以发现 \(dp_{n,|P|}\) 就是答案。
转移考虑每次减掉最小质因子是 \(P_x\) 的,也就是欧拉筛的过程。
这是一个容斥的过程,既然这个时候 \(P_x\) 是最小质因子,那么先乘上他的值,因为是完全积性所以不慌。
然后剩下的数要求就是最小质因子大于 \(P_x\) 即可,就是定义内的数再减去 \(< P_x\) 的质数的值。
其中 \(<P_x\) 的所有质数的值可以预处理出来,设为 \(pret(x)\)。
于是这个东西就做完了,注意这个玩意只对求质数有用, \(dp_{n,0}\) 当然不是答案,因为只有质数处点值可以写成 \(t(i)\)。
于是我们需要的只有 \(dp_{n,|P|}\), 记 \(g_{n} = dp_{n,|P|}\) ,也就是小于等于 n 的所有质数的点值。
第三步,利用积性模拟线性筛的过程去统计答案。
可以发现 \(S_{n,0}\) 加上1
处的点值就是答案。
转移就是按照第一步的思想去分成质数和非质数,非质数去枚举最小质因子和其幂次。
感觉本质就是线性筛的时候一次统计一种质因子而不是一个质因子,复杂度证明需要积分。
放个 luogu 模板题代码
ll n, val[N]; bool fg[N];
int sq, g1[N], g2[N], pos1[N], pos2[N], pr[N], sp1[N], sp2[N], znt, top;
const int inv3 = qpow(3, P - 2), inv2 = qpow(2, P - 2);
void sc(){
n = read<ll>(), sq = ceil(sqrt(n));
for(R i = 2; i <= sq; i++){
if(!fg[i]) pr[++top] = i, sp1[top] = A(sp1[top - 1], i), sp2[top] = A(sp2[top - 1], T(i, i));
for(R j = 1; j <= top && i * pr[j] <= sq; j++){
fg[i * pr[j]] = 1;
if(i % pr[j] == 0) break;
}
}
}
inline int pos(ll x){ if(x <= sq) return pos1[x]; else return pos2[n / x]; }
int S(ll n, int x){ if(pr[x] >= n) return 0;
int ans = A(A(g2[pos(n)], P - sp2[x]), P - A(g1[pos(n)], P - sp1[x])); // change to int and try fsan
for(R k = x + 1; k <= top && 1ll * pr[k] * pr[k] <= n; k++)
for(ll e = 1, pe = pr[k]; pe <= n; e++, pe *= pr[k]){
int zt = pe % P; jA(ans, T(T(zt, A(zt, P - 1)), A(S(n / pe, k), (e != 1))));
} return ans;
}
void work(){
for(ll l = 1, r; l <= n; l = r + 1){
r = n / (n / l), val[++znt] = n / l;
int k = val[znt] % P;
g1[znt] = T(((1ll * k * (k + 1)) % P), inv2);
g2[znt] = A(T(g1[znt], T(2 * k + 1, inv3)), P - 1);
jA(g1[znt], P - 1);
if(n / l <= sq) pos1[n / l] = znt; else pos2[r] = znt;
} //for(R i = 1; i <= n; i++) printf("%d %d\n", val[i], pos(val[i]));
for(R j = 1; j <= top; j++){
for(R i = 1; i <= znt && 1ll * pr[j] * pr[j] <= val[i]; i++){
jA(g1[i], P - T(pr[j], A(g1[pos(val[i] / pr[j])], P - sp1[j - 1])));
jA(g2[i], P - T(T(pr[j], pr[j]), A(g2[pos(val[i] / pr[j])], P - sp2[j - 1])));
}
} printf("%d\n", A(S(n, 0), 1));
}
题目
筛 \(\leq n \mod 4 = 1\) 的质数的个数。
还是同等思想, \(G_{i,j,1/3}\) 代表 \(\leq i\) 的数,最小值因子 \(\leq P_j\) 或者是质数 , \(\mod 4= 1/3\) 的数的个数。
每次去掉最小质因子转移,因为只需要质数处的点值,所以只用写第一个函数。
可以写出 \(W(n)\) 在去掉所有最小质因子之后的转移式子,发现需要同时 \(\sigma ,W\) 那么 \(Min_{25}\) 返回一个 pair, 不必局限于积性函数。
先口胡:杜教筛套(杜教筛加min25筛),筛 \(f(d)^k\) 只需要设 \(S(x,j)\) 和原来定义一样,但是只有合数的答案,就可以正常转移?
代码就写它了, code.