算法数学笔记-一、数论

一、初等数论

素数与约数的一些常用结论

素数数量,对1~n,约有素数 π(n) ≈ n/ln n

N! 中质因子p的个数为Np1+Np2+Np3++NplogpN

N=p1c1·p2c2·p2c2pkck

N 的正约数的个数为 (c1+1)(c2+1)(c3+1)(ck+1)

N的正约数的和为 (1+p11+p12p1c1)(1+p21+p22p2c2)(1+pk1+pk2+pk3pkck)

扩展欧几里得与裴蜀定理

扩展欧几里得

解同余方程ax + by = gcd(a, b)

int exgcd(int a, int b, int &x, int &y){
	if(b == 0) { x = 1, y = 0; return a;}
    int d = exgcd(b, a % b, x, y);
    int z = x; x = y; y = z - y * (a / b);
    return d; // d = gcd(a, b);
 }

aX + bY = s的通解:

exgcd(a, b, x, y);

X=s/gcdx+k1(b/gcd);(k1Z)

Y=s/gcdy+k2(a/gcd);(k2Z)

裴蜀定理

ab是不全为零的整数,若存在ax + by = c,则必有 gcd(a, b) | c

可扩展至多个数字(eg. ax + by + cz = d 必有 gcd(a, b, c) | d

扩展欧拉定理与费马小定理

费马小定理

papa (mod p)

扩展欧拉定理

ab{ab mod φ(m),gcd(a,m)=1ab mod φ(m) + φ(m),gcd(a,m)1 (mod m) am,ax1 (mod m)x0,x0 | φ(m)                                                 

逆元

扩展欧几里得

     ax1(mod m)ax+my=1,      a1=x

线型推法

inv[1] = 1;
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;

求任意n个数的逆元

计算前缀积s[i], 再计算s[n]的逆元sinv[n], sinv[i] = sinv[i + 1] * a[i + 1], inv[i] = sinv[i] * s[i - 1];

vec GetInvSum(vec a){
	
	int n = a.size() - 1;
	vec s(n + 1), sinv(n + 1);
	
	s[0] = a[0];
	for(int i = 1; i <= n; i ++)
		s[i] = 1ll * s[i - 1] * a[i] % mod;
	sinv[n] = GetInv(s[n]);
	
	for(int i = n - 1; i >= 0; i --)
		sinv[i] = 1ll * sinv[i + 1] * a[i + 1] % mod;
	
	for(int i = 1; i <= n; i ++)
		sinv[i] = 1ll * sinv[i] * s[i - 1] % mod;
		
	return sinv;
}

阶、原根、二次剩余

阶:

因此满足an1 (mod m)同余式 的最小正整数n存在,这个n称作am的阶,记作δm(a)=n

性质:

a1,a2,a3  aδm(a)  (mod m)两两互不同余

am1 (mod m)δm(a) | n; 若apaq (mod m)则有pq (mod δm(a))

!!!WA: 若gcd(a,m)==gcd(b,m)==1,则δm(ab)=δm(a)  δm(b)

原根

mN+,aZ,gcd(a,m)==1,δm(a)==φ(m),am

原根的判定:m>=3,gcd(a,m)==1,φ(p)paφ(m)p1(%m),am

原根的个数:mφ(φ(m))

原根存在定理:mm=2,4,pα,2pα(p,αN+)

最小原根数量级:mm14

模2的原根为1.

g%m%mgk%mgcd(k,φ(m)==1)

gcd(a,p)==gcd(b,p)==1,cZ,使δp(c)=lcm(δ(a),δ(b))

g%p使gp11(%p2)

inline long long getG(long long x){

	static long long q[10051];
	long long top = 0, flg = 1;
	for(register int i = 2; i * i <= x - 1; i ++){
		if((x - 1) % i == 0){
			q[++ top] = i;
			if(i * i != x - 1)
				q[++ top] = (x - 1) / i;
		}
	}	
	
	for(register int i = 2; ; i ++){
		flg = 1;
		for(register int j = 1; j <= top; j ++)
			if(quickpow(i, q[j], x) == 1){
				flg = 0;
				break;
			}
		if(flg)	return i;
	}
	

二次剩余

namespace Shengyu2{

	LL I, p, mod;

	struct Complex2{ // 此complex为二次剩余专用 
		LL real, fals;
		
		Complex2(double a = 0, double b = 0){
			real = a, fals = b;
		}
		
		Complex2 operator * (const Complex2 & x){
			LL a, b;
			a = (real * x.real + fals * x.fals % mod * I) % mod;
			b = (real * x.fals + fals * x.real) % mod;
			return Complex2(a > 0 ? a : a + mod, b > 0 ? b : b + mod);
		}
		
		Complex2 operator % (const LL & mod){
			real = real % mod;
			fals  = fals  % mod;
			return Complex2(real > 0 ? real : real + mod, fals > 0 ? fals : fals + mod);			
		}	
	};	
		
	Complex2 quickpow(Complex2 x, LL b, LL mod){
	
		if(b == 0) return Complex2(1, 0);
		if(b == 1) return x % mod;
		
		Complex2 l = quickpow(x, b >> 1, mod);
		l = (l * l) % mod;
		if(b & 1)
		l = (l * x) % mod;
		return l;
	}
	
	bool IsShengyu2(LL x, LL p){
		return (::quickpow(x, (p - 1) >> 1, p) == 1) || (x == 0);
	}

	pair<LL, LL> GetShengyu2(long long n, LL MOD){ // ling : get n 的二次剩余 
		p = mod = MOD;
		
		if(n == 0) return make_pair(0, 0);
		if(IsShengyu2(n, p) == false) 
			return make_pair(-1, -1); // (-1,-1) 表示无解 
		
		srand(time(0));
		long long a = rand();
		
		while(IsShengyu2((a * a - n + mod) % mod, p)) a = rand();
		
		Complex2 ans(a, 1);
		I = ((a * a - n) % mod + mod) % mod;
		
		ans = quickpow(ans, (p + 1) >> 1, mod);
		LL ans1 = ans.real, ans2 = mod - ans.real;
		// 理论上n的二次剩余有两个, 有时候会ans1 == ans2 
		return make_pair(min(ans1, ans2), max(ans1, ans2));
	}
}

类欧几里得算法

常用结论:

m=an+bcf(a,b,c,n)=i=0nai+bcg(a,b,c,n)=i=0nai+bc2h(a,b,c,d)=i=0niai+bc

f={(n+1)bca=0n(n+1)2ac+(n+1)bc+f(a%c,b%c,c,n)max(a,b)cnmf(c,cb1,a,m1)otherwise

g={(n+1)bc2a=0g(a%c,b%c,c,n)+2abh(a%c,b%c,c,n)+2bcf(a%c,b%c,c,n)+n(n+1)(2n+1)6ac2+n(n+1)acbc+(n+1)bc2max(a,c)cnm(m+1)f(a,b,c,n)2h(c,cb1,a,m1)2f(c,cb1,a,m1)otherwise

h={n(n+1)2bca=0h(a%c,b%c,c,n)+n(n+1)(2n+1)6ac+n(n+1)2bcmax(a,b)c12[mn(n+1)g(c,cb1,a,m1)f(c,cb1,a,m1)]ortherwise

扩展中国剩余定理

   xaii (mod mii) 

long long exCRT(int n, int mi[], int ai[]){
	for(int x, y, i = 2; i <= n; i ++){
		long long gcd = exGcd(mi[i], -mi[i - 1], x, y);
		long long lcm = abs(mi[i] / gcd * mi[i - 1]);
		x = quickmul(x, (ai[i - 1] - ai[i]) / gcd, lcm);
		ai[i] = ((quickmul(mi[i], x, lcm) + ai[i]) % lcm + lcm) % lcm;
		mi[i] = lcm;
	}
	
	return ai[n];
}

扩展Lucas

long long Vp_fact(long long n, long long q){ // 1~n中 质因子p 的个数
	long long re = 0;
	for(long long i = q; i <= n; i *= q)
		re += n / i;
		
	return re;
}

long long Askfact(long long n, long long q, long long mod){// 具体这个函数是干啥的我也不知道
	if(n <= q)
		return fact[n];
	else return Askfact(n / q, q, mod) * quickpow(fact[mod], n / mod, mod) % mod * fact[n % mod] % mod;
    // 此处的fact[i] 表示 1~i 中除了q的倍数之外的数的积
}
long long inv(long long x, long long mod){
	long long re = 0, k = 0;
	exGcd(x, mod, re, k);
	return (re % mod + mod) % mod;
}

long long multi_Lucas(long long n, long long m, long long mod, long long q, long long k){
	
	fact[0] = 1;
	for(long long i = 1; i <= mod; i ++)
		if(i % q == 0)
			fact[i] = fact[i - 1];
		else fact[i] = fact[i - 1] * i % mod;
    
	long long x = Vp_fact(n, q), y = Vp_fact(m, q), z = Vp_fact(n - m, q);
	long long XX = Askfact(n, q, mod), YY = Askfact(m, q, mod), ZZ = Askfact(n - m, q, mod);
	
	return XX * inv(YY, mod) % mod * inv(ZZ, mod) % mod * quickpow(q, x - y - z, mod) % mod;
}// ! ! 这里的inv一定要用exGcd()求解, 因为mod不一定是质数

long long exLucas(long long n, long long m, long long mod){
	
	long long P = mod;
	long long q[33], c[33], cnt = 0;
	long long mi[33], ai[33];
	
	for(long long i = 2; i * i <= P; i ++)
		if(P % i == 0){
			q[++ cnt] = i; c[cnt] = 0;
			while(P % i == 0)
				c[cnt] ++, P /= i;
		}
	if(P > 1) q[++ cnt] = P, c[cnt] = 1;
	
	for(long long i = 1; i <= cnt; i ++)
		mi[i] = quickpow(q[i], c[i]), ai[i] = multi_Lucas(n, m, mi[i], q[i], c[i]);
		
	return exCRT(cnt, mi, ai);
}

威尔逊定理及推论

对于素数p, 有 (p1)!1 (mod p)

对于整数n,令(n!)p表示所有小于等于 n 但不能被 p 整除的正整数的乘积

(n!)p=n!/(n/p! pn/p   (p))(p!)p=(p1)!1 (mod p)(pq!)p={1,if(p=2 && q3)1otherwise(mod pq)

推论

对于素数 p 和正整数 q 和非负整数 n

(n!)p ( (pq!)p )n/pq( (n%pq)! )p  (mod pq)

若存在1rp1 使(1)rr!1 (mod p)(pr1)!+10 (mod p)

升幂定理

vp(n)为正整数 np 的个数, 即pvp(n) 恰好正整除 n

  1. p 为奇素数
    ab (mod p)ab 不是 p 的倍数,vp(anbn)=vp(ab)+vp(n)

  2. p = 2

    ab 为素数

    n为奇数时v2(abbn)=v2(ab)

    n为偶数时v2(anbn)=v2(ab)+v2(a+b)+v2(n)1

扩展BSGS

long long exBSGS(long long a, long long b, long long p){
	
	map<long long ,long long > Map_bsgs;
	
	a %= p, b %= p;
	if(b == 1 || p == 1) return 0;
	
	long long cnt = 0, add = 1;
	for(long long d = Gcd(a, p); (d ^ 1); d = Gcd(a, p)){
		if(b % d) return -1;
		
		b /= d, p /= d, cnt ++;
		add = (a / d) * add % p;
		if(add == b) return cnt;
	}
	
	Map_bsgs.clear();
	long long t = ceil(sqrt(p));
	long long a_t = 1; 
	for(long long i = 0; i <= t - 1; i ++, a_t = a_t * a % p)
		Map_bsgs[b * a_t % p] = i;
	for(long long i = 0, now = add; i <= t; i ++, now = now * a_t % p)
		if(Map_bsgs.find(now) != Map_bsgs.end())
			if(i * t - Map_bsgs[now] >= 0) 
				return i * t - Map_bsgs[now] + cnt;
			
	return -1;
	
}

狄利克雷卷积计算

在差不多线型的复杂度求狄利克雷卷积An=d|nf(d)g(nd)fg 都是数论积性函数

实际时间消耗大概是线筛的两倍,1.5e7/s

#define f(x)
#define g(x) 

g(1) = 1;
f(1) = 1;

for(int i = 2; i <= n; i ++){
		if(vis[i] == 0){
			lpn[i] = i; prime[++ cnt] = i;
			A[i] = (g(i) + f(i)) % mod;
		}
		
		for(int j = 1; j <= cnt && prime[j] * i <= n; j ++){
			vis[prime[j] * i] = 1;
			
			if(i % prime[j] == 0){
			
				int x = i * prime[j], p = prime[j];
				lpn[x] = lpn[i] * p;
	
				if(lpn[x] != i * prime[j])
					A[x] = 1ll * A[lpn[x]] * A[x / lpn[x]] % mod;
				else{
					A[i * prime[j]] = 0;	
					// 注意 ↓ 这里的 LL 不要写成 int, 如果可以的话全开LL比较方便
					for(long long now = 1; now <= lpn[x]; now *= p)
						A[i * p] = (A[i * p] + 1ll * f(now) * g(lpn[x] / now)) % mod;	
					A[i * p] = 1ll * A[i * p] * A[i * p / lpn[x]] % mod;			
				}
				break;	
			}
			else
				lpn[i * prime[j]] = prime[j], A[i * prime[j]] = 1ll * A[i] * A[prime[j]] % mod;
		}
	}

莫比乌斯反演,欧拉反演 与 狄利克雷卷积

莫反结论

[gcd(i,j)=1]=d|id|jμ(d)

gcd(i,j)=d|id|jφ(d)

f(n)=d|ng(d)     g(n)=d|nμ(d)f(nd)

f(d)=d|ng(n)     g(d)=d|nμ(nd)f(n)

j=1ngcd(i,j)=j=1nd|id|jφ(d)=d|iφ(d)nd

j=1ngcd(0,j)=d=1nφ(d)nd=j=1nj=n(n+1)2

i=1nj=1mgcd(i,j)=i=1nj=1md|i|jφ(d)=d=1min(n,m)φ(d)ndmd

i=1nj=1mgcd(i,j,k)=i=1nj=1md|id|jd|kφ(d)=d|kφ(d)ndmd

i=1nj=1m[gcd(i,j)=1]=i=1nj=1mx|ix|jμ(x)=x=1min(n,m)μ(x)nxmx

k=1n[gcd(i,k)=d]=k=1n/d[gcd(id,k)=1]=k=1n/dg|id[g|k]μ(g)=g|idμ(g)n/dg

i=1nj=1m[gcd(i,j)=d]=i=1n/dj=1m/dx|ix|jμ(x)=x=1min(n,m)/dμ(x)ndxmdx=d|Tmin(n,m)μ(Td)nTmT

i=1nj=1mgcd(i,j)=d=1min(n,m)di=1nj=1n[gcd(i,j)=d]=d=1min(n,m)d|Tdμ(Td)nTmT

欧拉反演

n=d|nφ(d)i=1ngcd(i,n)=d|nndφ(d)

常用数论函数

狄利克雷卷积单位元:ϵ(n)=[n==1]

 Idk(n)=nk

恒等函数:Id(n)=n

除数函数:σk(n)=d|ndk; 当k == 1时,即为除数和 函数σ(n);当k==0时,即为除数个数函数σ0(n)=d(n)

常值函数:I(n)=1(n)=1

1(n)Idk(n)=σk(n)1(n)φ(n)=d|nφ(d)=n=Id(n)μ(n)Id(n)=φ(n)d(ij)=x|iy|j[gcd(x,y)==1]μ(n)1(n)=ϵ(n)

逆元

f(n)g(n)=ϵ(n),若f是积性函数,则g=f1也是积性函数

f1={1f(n)n=11f(1)d|n,d1f(d)f1(nd)ortherwise

狄利克雷前缀和

复杂度O(n log log n)

(倒)狄利克雷前缀和

b[n]=d|na[d]

b[i] = a[i];// 已知 a 求 b
for(int i = 1; i <= cnt; i ++)
	for(int j = 1; j * Prime[i] <= N; j ++)
		b[j * Prime[i]] += b[j];
a[i] = b[i]; // 已知 b 求 a
for(int i = cnt; i >= 1; i --)
	for(int j = N / Prime[i]; j >= 1; j --)
		a[j * Prime[i]] -= a[j];

(倒)狄利克雷后缀和

b[d]=d|na[n]

b[i] = a[i]; // 已知 a 求 b
for(int i = 1; i <= cnt; i ++)
    for(int j = N / Prime[i]; j >= 1; j --)
            b[j] += b[j * Prime[i]];
a[i] = b[i]; // 已知 b 求 a
for(int i = cnt; i >=1; i --)
	for(int j = 1; j * Prime[i] <= N; j ++)
		a[j] -= a[j * Prime[i]];

杜教筛

S(n)=i=1nf(i)

g(1)S(n)=i=1n(fg)(i)i=2ng(i)Sni)

积性函数在组合数处的取值

定义vp(n)=k>=1npkvp(n) 是1~n中质因子p的个数, 记fp(α)=f(pα)于是有

f(Cnm)=pPfp(vp(n)vp(m)vp(nm))

其中vp(n)vp(m)vp(nm)=k>=1(npkmpknmpk)

又因npkmpknmpk{0,1}

所以 vp(n)vp(m)vp(nm)<=logpn

p1:p<=n

p2:p>n

莫队算法O(N3/2)

1.Cnmp2(O(nn))

2.p1(O(nlnnQ))

莫队维护过程中:

每次n,m的变化,对于新的n,m,只需修改n,m,n- m这三个数的质因子p2的贡献,显然某个数最多有1个p2所以是O(1)

一阶判别式O(N3/2lnn)

1.p1O(nlnnQ)

2.p2:

因为:

npkmpknmpk{0,1}

[npkmpknmpk=1]fp(1)=fp(1)npkmpknmpk

[npkmpknmpk=0]fp(0)=f(1)=1=fp(1)npkmpknmpk

所以:p2fp(npkmpknmpk)=p2fp(1)npkmpknmpk

S(n)=p2fp(1)np

于是p2的贡献既是S(n)S(m)S(nm)

S预处理:每次只对(0 / 1)个p2 修改O(n)

PN筛

PN筛求积性函数f的前缀和:

构造一个g函数

​ 1.g是积性函数

​ 2.g的前缀和易求

​ 3.对于质数p,g(p) == f(p)

#include<bits/stdc++.h>

using namespace std;

#define LLL __int128
 
const LLL mod = 4179340454199820289, W = 5e6;

LLL sum_g[W + 1], prime[W + 1], Inv[111];
bool vis[W + 1];
LLL sum, n, m, T, cnt, tot, tot_PN, ans, inv6, inv2;
int yy;

map<LLL , LLL >Map_sum_g;
vector <LLL > h[W];

LLL exGcd(LLL a, LLL b, LLL &x, LLL &y){
	if(b == 0) {x = 1, y = 0; return a; };
	LLL d = exGcd(b, a % b, x, y);
	LLL z = x; x = y; y = z - y * (a / b);
	return d;
}

LLL inv(LLL x, LLL mod){
	LLL re = 0, k = 0;
	exGcd(x, mod, re, k);
	return (re % mod + mod) % mod;
}

void getprime(LLL n){
	
	tot = 0;
	for(LLL i = 2; i <= n; i ++){
		if(vis[i] == false)
			prime[++ tot] = i;
		for(LLL j = 1; j <= tot && prime[j] * i <= n; j ++){
			vis[i * prime[j]] = true;
			if(i % prime[j] == 0) break;
		}
	}	
}

bool ok(LLL x){
	return 0 < x && x <= n;
}

LLL Getf(LLL p, LLL c, LLL pc){
	return pc * Inv[c] % mod;
}

LLL Getg(LLL p, LLL c, LLL pc){
	return pc % mod;
}

LLL GetG(LLL n, LLL d, LLL x){
		return x * (x + 1) / 2 % mod; 
}

void dfsPN(LLL c, LLL now, LLL hh){
	
	ans = (ans + hh * GetG(n, now, n / now)) % mod;	
	for(LLL i = c + 1, p = prime[c + 1]; i <= tot && ok(now * p) && ok(now * p * p); i ++, p = prime[i])
		for(LLL k = 2, pk = p * p; ok(now * pk); k ++, pk *= p)
			dfsPN(i, now * pk, hh * h[p][k] % mod);
	
}

inline LLL quickpow(LLL x, long long b, LLL mod){
	LLL j = 1;
	; 
	for(; b; b >>= 1, x = x * x % mod)
		if(b & 1) 
			j = j * x % mod;
			
	return j % mod;
}

void work(){
	
	long long t;
	cin >> t;
	n = t;
	
	ans = 0; 
	dfsPN(0, 1, 1);

	long long ANS = ans * quickpow(n, mod - 2, mod) % mod % mod;
	cout << ANS << endl;
	
}

signed main(){
	
	n = 1e12 + 1;
	
	Inv[1] = 1;
	for(int i = 2; i <= 100; i ++)
		Inv[i] = (mod - mod / i) * Inv[mod % i] % mod;
	
	inv6 = inv(6, mod);
	inv2 = inv(2, mod);
	getprime(W - 1);
	
	for(LLL u = 1; u <= tot && prime[u] * prime[u] <= n; u ++){
		LLL p = prime[u];
		h[p].push_back(1);
		for(LLL c = 1, p_c = p; p_c <= n; p_c *= p, c ++){
			h[p].push_back(Getf(p, c, p_c));
			for(LLL i = 1, p_i = p; i <= c; i ++, p_i *= p)
				h[p][c] = ((h[p][c] - Getg(p, i, p_i) * h[p][c - i]) % mod + mod) % mod;
		}
	}	
	
	int T = 1;
	cin >> T;
	while(T --) work();
}

min25筛

#include<bits/stdc++.h>
using namespace std;

#define int long long

int quickpow(int x, int b, int mod);

const int N = 2e6+100, mod = 1e9+7, inv6 = quickpow(6, mod - 2, mod), inv2 = quickpow(2, mod - 2, mod);

int Q, tot_prime, n, sw, ans = 0, lenth = 2, w[N];
int g[11][N], id1[N], id2[N], gp[11][N], prime[N], vis[N], K[11], c[11], G[N], F_prime[N];
int kk;

inline int getid(int x){
	return (x <= Q ? id1[x] : id2[n / x]);
}

int quickpow(int x, int b, int mod){

	if(b == 0) return 1;
	if(b == 1) return x % mod;

	int l = quickpow(x, b >> 1, mod);
	l = l * l % mod;
	if(b & 1) return l * x % mod;
	else return l;
}

inline int f_p(int p){//真实的f(p)的表达式 
// 注意取模 
	p %= mod;
	return p * (p - 1) % mod;
}
 
int f_p_k(int p_k, int p, int k){//真实的f(p_k)的表达式 
// 注意取模 
	p_k %= mod;
	if(k == 1) return f_p(p);
	else return p_k * (p_k - 1) % mod;
}

int S(int x, int y){
// S(x, y) : 1 ~ x 中 minp > prime[y]的数字的多项式的和 
	if(prime[y] >= x) return 0;

	int p = getid(x);
	int re = (G[p] - F_prime[y] + mod) % mod;

	for(int i = y + 1; i <= tot_prime && prime[i] * prime[i] <= x; i ++){
		int p_k = prime[i];

		for(int k = 1; p_k <= x; k ++, p_k *= prime[i]){
			int o = p_k % mod;
			int add = 0;
			add = f_p_k(p_k, prime[i], k);
			re = (re + add * (S(x / p_k, i) + (k != 1))) % mod;
		}
	}
	return re;
}

int sum_mi(int n, int k, int mod){
	if(k == 0) return n % mod;
	else if(k == 1) return(n + 1) * n % mod * inv2 % mod;
	else if(k == 2) return(n + 1) * n % mod * (2 * n + 1) % mod * inv6 % mod;
	else if(k == 3) return quickpow(n % mod * (n + 1) % mod * inv2 % mod, 2, mod);
}

void work(){

	int ans = 0;
	cin >> n; 
	Q = sqrt(n);
	
	lenth = 2;
	c[1] = 2, K[1] = 1;
	c[2] = 1, K[2] = -1;
	
	//这里的是f(p)(不是f(p_k))的多项式,但只用于预处理,与真实的f(p)无直接关系 
	
	
	{// clear
		tot_prime = sw = 0;
		for(int i = 1; i <= Q; i ++)
			vis[i] = 0;
		for(int i = 1; i <= 3 * Q; i ++){
			for(int j = 1; j <= lenth; j ++)
				g[j][i] = 0;	
			G[i] = 0;		
		}
	}
	
	
	prime[0] = 1;
	for(int i = 2; i <= Q; i ++){
		if(!vis[i]) prime[++ tot_prime] = i;
		for(int j = 1; j <= tot_prime && i * prime[j] <= Q; j ++){
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}


	for(int i = 1; i <= tot_prime; i ++)
		for(int j = 1; j <= lenth; j ++)
			gp[j][i] = (gp[j][i - 1] + quickpow(prime[i], c[j], mod)) % mod;
	
	// gp[j][i] 表示prime[1] ~ prime[i] 的单项式的和 i <= tot_prime (prime <= sqrt(n)) 


	for(int l = 1, r; l <= n; l = r + 1){
		r = n / (n / l);

		w[++ sw] = n / r;
		int x = w[sw] % mod;
		for(int i = 1; i <= lenth; i ++)
			g[i][sw] = (sum_mi(x, c[i], mod) - 1 + mod) % mod;

		if(n / r <= Q)
			id1[n / r] = sw;
		else id2[r] = sw;
	}
	
	
/*
	查询1 ~ n / x的单项式的和 --> g[j][sw]
	if(n / x <= Q) : sw = id1[n / x];
	if(n / x >  Q) : sw = id2[x]; 
	w[sw] = n / x

	y = n / x
	查询1 ~ y的单项式的和 --> g[j][getid(y)]
	w[getid(y)] = y
*/ 

	
	for(int i = 1; i <= tot_prime; i ++)
		for(int j = 1, squ = prime[i] * prime[i]; j <= sw && w[j] >= squ; j ++){
			int p = w[j] / prime[i];
			p = getid(p);

			for(int v = 1; v <= lenth; v ++)
				g[v][j] = (g[v][j] - quickpow(prime[i], c[v], mod) * (g[v][p] - gp[v][i - 1] + mod) % mod + mod) % mod;
	}
	
	// 查询1 ~ n / x的所有 质数 的单项式的和 --> g[j][sw]
	 
	 
	for(int j = 1; j <= sw; j ++)
		for(int i = 1; i <= lenth; i ++)
			G[j] = (G[j] + K[i] * g[i][j]) % mod;
			
	// 询1 ~ n / x的所有 质数 的多项式的和 --> G[sw]	
	
/*	
	for(int j = 1; j <= sw - 1; j ++)
	G[j] = (G[j] + 2) % mod;
	这里是G[n / x]中f(p)由多项式之和 向 真实表达式之和 的转化 
*/
	for(int i = 1; i <= tot_prime; i ++)
		F_prime[i] = (F_prime[i - 1] + f_p(prime[i])) % mod;
	//F_prime[i] 小于等于prime[i]的质数的真实表达式之和 , prime[i] <= Q
	
	
	cout << ((S(n, 0) + 1) % mod + mod) % mod;

	return ;
}

signed main(){
	
	work();
}
// 100000000000

Miller Rabin素数测试 & Pollard Rho因数分解


namespace Fctr{
	
	#define int long long 
		
	set<int > Prime;
	
	mt19937 rnd(random_device{}());
	
	int qmul(int x, int y, int p){
		int r = x * y - p * (int)(1.L / p * x * y);
		return r - p * (r >= p) + p * (r < 0);
	}
	
	int quickpow(int x, int y, int p){
		int r = 1;
		for(; y; y >>= 1, x = qmul(x, x, p))
			if(y & 1) r = qmul(r, x, p);
		return r;
	}
	
	bool MR(int n){
		
		if(n < 4) return n > 1;
		
		int k = __builtin_ctz(n - 1), m = n - 1 >> k;
		for(int i = 0; i <= 4; i ++){
			int e = quickpow(rnd() % (n - 3) + 2, m, n), j = k;
			for(; e != 1 && e != n - 1 && j --; e = qmul(e, e, n));
			if(e != n - 1 && j != k)
				return false;
		}
		return true;
	}
	
	int f(int x, int n){
		return qmul(x, x, n) + 1;
	}
	
	int PR(int n){
		
	  int p = 2, q;
	  for(int x = 0, y = 0, i = 0; i ++ % 255 || __gcd(p, n) == 1;){ // !!!
	  	 x = f(x, n), y = f(f(y, n), n);
	    if(x == y){
	   		x = rnd() % n;
			y = f(x, n);	
		}
		
		q = qmul(p,abs(x-y),n);
	    if(q)p=q;
		}
		return __gcd(p,n);
	}
	
	void fctr(int n){
		if(MR(n)){
			Prime.insert(n);
			return ;
		}
		
		int e = PR(n);
		fctr(n / e);
		fctr(e);
	}
}

*2022/10/5 新增积性函数在组合数处的取值
*2022/10/16 修改Miller Rabin素数测试 & Pollard Rho因数分解模板,速度大幅度优化
*2022/11/24 新增了莫比乌斯反演和欧拉反演的诸多公式
*2022/11/24 改进了min_25筛模板

posted @   lyhy  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示