网络赛数论板子题自闭
特此开坑
ACM数论知识点
参考博客
快速幂
ll qpow(int a,int p){
ll ans = 1;
while(p){
if(p & 1)ans = (ans * a) % mod;
a = (a * a) % mod;
p >>= 1;
}
return ans;
}
gcd
int gcd(int a,int b){
return a % b == 0 ? b : gcd(b, a % b);
}
整除分块
for(int l = 1,r = 0;l <= n;l = r + 1){
r = n/(n/l);
//...
}
1. 积性函数
如果已知一个函数为数论函数,且(f(1)=1),并且满足以下条件,若对于任意的两个互质的正整数(p,q)都满足(f(p⋅q)=f(p)⋅f(q)),那么则称这个函数为积性函数。
常见的积性函数
(1. mu(n))
莫比乌斯函数
$mu(1) = 1 $
(d=Pi_{i=1}^{k}p_i), 且 (p_i) 为互异素数时,(mu(d) = (-1)^k)
(d) 含有任何质因子的幂次大于等于 (2) ,则 (mu(d) = 0)
(mu * I = epsilon)
$2. varphi(n) $
欧拉函数
表示 ([1,n)) 内与 (n) 互质的数的个数
(varphi * I = id)
( ightarrow varphi * I * mu = id * mu)
( ightarrow varphi = id * mu=sum_{d|n}mu(d)cdot dfrac n d)
[frac{varphi(n)}{n}=sum_{d|n}frac{mu(d)}{d} ]
(3. d(n))
约数个数
证明:
(4. sigma(n))
约数和函数
完全积性函数
(1. epsilon(n))
元函数, (epsilon(n) = [n==1])
(f * epsilon = f)
(2. I(n))
恒等函数, (I(n) = 1)
(I * f = sum_{d|n}f(d))
(3. id(n))
单位函数, (id(n) = n)
(lcy) の (ppt)
2. 欧拉函数
欧拉函数(varphi(n))等于小于等于n的所有正整数中和n互素的数的个数
一些性质
性质一:
对于素数(p)
很显然,除了(p) 1到(p-1)和都(p)互素
性质二:
对于素数(p)
证明:
对于[1,(p^k)]中的数n,若与(p^k)不互素,则必有
而(xin[1,2,...,p^{k-1}])
所以(varphi(p^k) = p^k - p^{k-1})得证,第一个式子是总数,第二是不互素的数的个数
性质三:(积性)
对于素数(p),(q)
性质四: (计算公式)
对于任意正整数(n) 其中 (n = prod_{i=1}^{i = k}p_i^{e_i})
所以在欧拉筛中
if (i % prime[j] == 0) {//因数都一样,多乘了一个p phi[i * prime[j]] = phi[i] * prime[j]; break; } else {//积性 phi[i * phi[j]] = phi[i] * (phi[j] - 1); }
证明:
证毕
性质五:
若 (i mod p = 0)
则
若 (i mod p
eq 0)
则
这条性质显然,由积性直接得出((i),(p)互质)
对于第一条性质,首先要知道一个引理:
证明:
若(n+i)与(i)有公因数(k),则(n)与(i)也有公因数(k),矛盾
由此
若(gcd(a,b)=1),则(gcd(a,b+ka)=1),(kin Z)((k)可以是负数)
因为(i mod p = 0),所以对于(kin[1,i]),(gcd(k,i)=1)和(gcd(k,i*p))是等价的
又(gcd(k+xi,i)=1),(xin[0,1,2,..,p-1])
对于(k>i)的(k)来说,一定是由(kin[1,i])转移而来
所以 (varphi(i*p)=p*varphi(i))
欧拉定理:
若 (gcd(a,n) == 1) ,则
广义欧拉定理
一般用作降幂,也可以用来求逆元
(sum_{d|n} varphi(d) = n)
3. 莫比乌斯函数
先介绍莫比乌斯函数 (mu) ,是一个常用的积性函数
一、性质
①、常用**
其中([n=1])表示n == 1时候成立,为1,否则为0
也就是说任何大于1的整数n,其所有因子的莫比乌斯函数之和为0
②、
对于任意正整数n,
这个性质把莫比乌斯函数和欧拉函数联系在了一起
莫比乌斯反演
定理:
(F(n)) 和 (f(n)) 是定义在非负整数集合的两个函数,且满足
则有
利用卷积的性质不难整明
以及
4. 埃氏筛
bool isprime[maxn];
int prime[maxn],cnt;
void get(int N){
for(int i = 0;i <= N;i++)isprime[i] = true;
isprime[0] = isprime[1] = false;
for(int i = 2;i <= n;i++){
if(isprime[i]){
prime[++cnt] = i;
for(int j = i * i;j <= n;j += i){
isprime[j] = false;
}
}
}
}
5. 线性筛
基本思想:
对于每一个数n,筛去所有小于等于n最小质因数的质数和n乘积的数
我感觉线性筛其实是筛积性函数的通用的方法
直接给出代码
线性筛中最精华的部分就是这一句话
if(i % prime[j] == 0)break;
它确保了每个数只被它的最小质因数访问
例如 i = 2*3*5
, 则需要筛去 2*i
,不能筛去 3*i
观察 (n=30) , (n) 的因数有 (2,3,5) ,则只会被 (15 * 2) 筛去一次
而不能被 (10 * 3) 筛去。
$ n = p_1{k_1}p_2{k_2}...p_n^{k_n}$
则 (n) 只会被 (p_1^{k_1-1}p_2^{k_2}...p_n^{k_n}) 筛去
void get_mu(int n){
mu[1] = 1;
for(int i = 2;i <= n;i++){
if(!vis[i]){
prime[++cnt] = i;
min_fac[i] = i;
}
for(int j = 1;j <= cnt && prime[j]*i <= n;j++){
vis[prime[j]*i] = 1;
if(i % prime[j] == 0)break;
min_fac[prime[j]*i] = prime[j];
}
}
}
线性筛还可以用来分解质因数
因为可以记录每个数的最小质因数。
void fac(int n){
while(n > 1){
p[min_fac[n]]++;
n /= min_fac[n];
}
}
6. 迪利克雷卷积
两个函数 (f) , (g) 的迪利克雷卷积
满足交换律,结合律,分配律,可以当作乘法
7. 莫比乌斯反演
若
则
另一种形式:
若
可以推出
8. 杜教筛
要求的东西 : (sum f(i))
设
记 (S(n) = sum_{i=1}^{n}f(i))
这一步的两个 (sum) 的位置变换可以自己在纸上举了例子体会一下
9. min25筛
一种低于线性复杂度的求积性函数前缀和的筛法。
适用条件:
- (f(p)) 是多项式
- (f(p^k)) 便于计算
思想:
分为两个部分,第一部分是所有素数,第二部分是所有的合数
第一部分
搞来一个这样的函数 (g(n,j))
所有的素数加上满足(minp(i) > P_j) 的所有 (i)
([1-n]) 中所有质数的 (k) 次方之和就是 (g(n,x)) ,(P_x) 是最后一个小于等于
(sqrt n) 的质数
考虑 (g(n,j)) 的转移
这个东西自己在纸上写一些体会一下,注意 (P_j) 筛去的第一个数是 (P_j^2) , 第二个数不是 (P_j^2+ P_j)
第二部分
设
可以把 (S(n,x)) 也分成两部分,一部分是所有大于 (P_x) 的质数,另一部分是最小质因数大于 (P_x) 的合数,枚举最小质因子
当 (e = 1) 的时候, (P_k) 在前面枚举过了,不等于 (1) 时,需要加上 (P_k^e)
存下所有可能的 (lfloordfrac n x floor) , 做一个映射
要注意取模的时候不要爆数据范围
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7, inv6 = 166666668, inv2 = 500000004;
const int maxn = 1e6 + 10;
ll n, sqr;
ll prime[maxn], cnt, vis[maxn];
ll sp1[maxn], sp2[maxn];//sp1 p的前缀和,sp2 p^2的前缀和
ll w[maxn], tot;
ll g1[maxn], g2[maxn], ind1[maxn], ind2[maxn];
void get(int maxn) {
for (int i = 2; i <= maxn; i++) {
if (!vis[i]) {
prime[++cnt] = i;
sp1[cnt] = (sp1[cnt - 1] + i) % mod;
sp2[cnt] = (sp2[cnt - 1] + 1ll * i * i) % mod;
}
for (int j = 1; j <= cnt && prime[j] * i <= maxn; j++) {
vis[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
ll S(ll x, int y){
if (prime[y] >= x)return 0;
ll k = x <= sqr ? ind1[x] : ind2[n / x];
ll ans = (g2[k] - g1[k] + mod - (sp2[y] - sp1[y]) + mod) % mod;
for (int i = y + 1; i <= cnt && prime[i] * prime[i] <= x; i++)
{
ll pe = prime[i];
for (int e = 1; pe <= x; e++, pe = pe * prime[i])
{
ll xx = pe % mod;
ans = (ans + xx * (xx - 1) % mod * (S(x / pe, i) + (e != 1))) % mod;
}
}
return ans % mod;
}
int main() {
scanf("%lld", &n);
sqr = sqrt(n);
get(sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++tot] = n / l;
ll k = w[tot] % mod;
g1[tot] = (k * (k + 1) % mod * inv2 - 1 + mod) % mod;
g2[tot] = (k * (k + 1) % mod * (2 * k + 1) %mod * inv6 % mod + mod - 1) % mod;
if (w[tot] <= sqr)ind1[n / l] = tot;
else ind2[n / (n / l)] = tot;
}
for (int i = 1; i <= cnt; i++) {
//g(n,j) 滚第一维
for (int j = 1; j <= tot && prime[i] * prime[i] <= w[j]; j++) {
ll k = w[j] / prime[i] <= sqr ? ind1[w[j] / prime[i]] : ind2[n / (w[j] / prime[i])];
g1[j] -= prime[i] * (g1[k] - sp1[i - 1] + mod) % mod;
g2[j] -= prime[i] * prime[i] % mod * (g2[k] - sp2[i - 1] + mod) % mod;
g1[j] %= mod; g2[j] %= mod;
if (g1[j] < 0)g1[j] += mod;
if (g2[j] < 0)g2[j] += mod;
}
}
printf("%lld
", (S(n, 0) + 1) % mod); //f(1) = 1
}