Processing math: 87%

『你从未见过的船新数论』

Parsnip·2020-10-08 14:15·500 次阅读

『你从未见过的船新数论』

Preface#

References:zqy's blogs : cnblogs,云烟万象但过眼.

前排提示:本文数学公式较多,加载LATEX需要一定时间,可能会导致浏览器暂时卡顿,请耐心等待数学公式正常显示.

快速幂和快速乘#

原理:二进制分解和取模的定义

a×bmodp=a×bp×a×bp

Copy
inline int fastpow(int a,int b) { int c = 1; for (; b; Mul(a,a), b>>=1) if (1&b) Mul(c,a); return c; } inline ll fastmul(ll a,ll b) { return ( a * b - (ll)( (long double) a * b / p ) * p + p ) % p; }

整除和约数#

整除#

如果ab的约数,则称a整除b,记作a|b

整除的性质:

1. a|b,b|ca|c

2. a|b,c|dac|bd

3. a|b,a|ca|(bn+cm)

4. ma|mba|b

约数#

d|a,d|bd|(ab)

所以有gcd(a,b)=gcd(ab,b)=gcd(amodb,b),每次取模a减小一半,时间复杂度O(logn)

lcm(a,b)=a×bgcd(a,b)

Copy
inline int gcd(int a,int b) { return b == 0 ? a : gcd( b, a % b ); } inline int lcm(int a,int b) { return a / gcd(a,b) * b; }

素数和筛法#

素数定理#

π(n)=ni=1[iPrime]nlnn

Eratosthenes筛法#

ni=1[iPrime]ilnlnn

线性筛#

用每个数字的最小质因子来筛掉它,时间复杂度O(n)

Copy
inline void EulerSieve(int Lim) { for (int i = 2; i <= Lim; i++) { if ( !flag[i] ) Prime[++cnt] = i; for (int j = 1; j <= cnt && i * Prime[j] <= Lim; j++) { flag[ i * Prime[j] ] = true; if ( i % Prime[j] == 0 ) break; } } }

算术基本定理和推论#

每个正整数n都有如下的唯一分解:

n=ki=1paii,i[1,k] piPrime

有如下推论成立:

τ(n)=ki=1(ai+1),σ(n)=ki=1(aij=0pji)a=ki=1paii,b=ki=1pbiigcd(a,b)=ki=1pmin(ai,bi)i,lcm(a,b)=ki=1pmax(ai,bi)i

同余#

同余的定义和性质#

m|(ab),则称a,bm同余,记作ab(modm)

同余的基本性质:自反性,对称性,传递性,同加性,同乘性,同幂性。

同余的其他性质:

1. ab(modn),ab(modn)ab(modlcm(n,m))

2. n|m,ab(modn)ab(modn)

3. gcd(c,m)=d,acbc(modm)ab(modmd)

Fermat定理#

p是质数,则对于任意整数aapa(modp)

Wilson定理#

p是素数,则(p1)!1(modp)

同余类和剩余系#

对于任意a[0,p1],集合{a+kp|kZ}中所有数模p都同余,余数为a,称该集合为模p意义下的一个同余类,简记为¯a

p意义下的同余类一共有p个,分别为¯0,¯1,¯2,,¯p1,他们构成模p意义下的完全剩余系,简称完系

1p中与p互质的数有φ(p)个,这些数字对应的同余类组成了p化简剩余系,也称既约剩余系缩系

既约剩余系的重要性质是关于模p乘法封闭,也就是a,bp互质,显然a×b,a×bmodp都与p互质,所以a×bmodp也在既约剩余系里面。

Euler定理#

ab{abmodφ(p),gcd(a,p)=1ab,gcd(a,p)1,b<φ(p)abmodφ(p)+φ(p),gcd(a,p)1,bφ(p)(modp)

可以用来缩小指数规模。

对于gcd(a,p)=1的情况,可以用剩余系的知识来证明:

p的既约剩余系为{¯a1,¯a2,,¯aφ(p)},对于ai,aj,若a×aia×aj(modp),则a(aiaj)0(modp),由于gcd(a,p)=1,所以aiaj(modp)。故对于aiaj,都有a×aia×aj表示不同的同余类。

又因为既约剩余系乘法封闭,所以¯aai,¯aaj都在既约剩余系中,因此{¯aa1,¯aa2,,¯aaφ(p)}也表示p的既约剩余系,故:

φ(p)i=1aaiaφ(p)φ(p)i=1aiφ(p)i=1ai(modp)

所以aφ(p)1(modp),证毕。

同余方程#

裴蜀定理#

方程ax+by=c有整数解,当且仅当c|gcd(a,b),此时有无数多组整数解。

证明:

显然要证方程ax+by=gcd(a,b)有解,那么x=xcgcd(a,b),y=ycgcd(a,b)就是原方程的整数解。

不妨设ax1+by1=gcd(a,b),bx2+(amodb)y2=gcd(b,amodb),所以:

ax1+by1=bx2+(amodb)y2=bx2+(ab×ab)y2ax1+by1=ay2+b(x2aby2)

显然只要方程bx2+(amodb)y2=gcd(b,amodb)有解,就可以构造一组原方程的解。

此时归纳推理,问题转为求方程gcd(a,b)x=gcd(a,b),显然x=1,yZ就是一组解,那么原命题得证。

拓展欧几里得算法#

把上述迭代过程实现一下,时间复杂度O(logn)

Copy
inline int Exeuclid(int a,int b,int &x,int &y) { if ( b == 0 ) return x = 1, y = 0, a; int p = Exeuclid(b,a%b,y,x); return y -= a / b * x, p; }

gcd(a,b)=p,原方程的一组特解为(x0,y0),则原方程的通解为:

x=x0+bp×k,y=y0ap×k,kZ

乘法逆元#

求方程ax1(modp)的解。

方法1:当pPrime时,xa1ap2(modp),快速幂即可。

方法2:原方程有解等价于p|(ax1),不妨设ax1=py,那么ax+py=1,显然原方程有解等价于gcd(a,p)=1,那么可以用扩展欧几里得算法求解。

方法3:不妨设p=ka+t(t<a),那么ka+t0(modp),等式两边同时乘a1t1,那么a1kt1pa(pmoda)1(modp),递归下去,时间复杂度O(logn)。如果改成递推,可以O(n)1n所有数字的逆元。

线性同余方程组#

求解方程组

{xa1(modm1)xa2(modm2)   xan(modmn)

当所有mi互质的时候,可以令M=mi,Mi=Mmi,ti=M1i(modmi),则x=aiMiti是原方程组的一个解,这个结论叫做中国剩余定理。

mi不满足两两互质的时候,可以归纳求解,不妨设前i1个方程的解为xlcmi1j=1{mj}=m,显然x+km(kZ)都是前i1个方程的解,只要让x+kmai(modmi)成立就求出了前i个方程的解,这时候解一个单个的同余方程即可,如果无解,那么原方程组就无解。

这样解方程很容易有整型溢出的风险,要注意数据范围,即使lcmlong long范围内,也用用快速乘。

Copy
inline ll ExCRT(void) { ll res = a[1] % m[1], l = m[1], p, x, y, v; for (int i = 2; i <= n; i++) { v = ( ( a[i] - res ) % m[i] + m[i] ) % m[i]; if ( v % gcd(l,m[i]) ) return -1; p = Exeuclid(l,m[i],x,y); x = ( x % (m[i]/p) + (m[i]/p) ) % (m[i]/p), x = fastmul( x, v/p, m[i]/p ); res = res + fastmul( x, l, lcm(l,m[i]) ), res %= ( l = lcm(l,m[i]) ); } return res; }

离散对数#

求方程axb(modp)的解,gcd(a,p)=1

根据欧拉定理,aφ(p)1(modp),所以x一定在区间[1,φ(p)]里面,直接枚举的话时间复杂度是O(φ(p))的。

考虑分块,令x=kTr(rT),那么akTrb(modp),所以akTarb(modp)。此时可以O(T)的时间枚举arb的取值,存在一个hash表里面,那么可以枚举k[1,φ(p)T+1],然后查表即可,取T=φ(p),时间复杂度O(φ(p))

Copy
inline ll BSGS(ll a,ll b,ll p) { ll T = sqrt(p), v1 = 1, v2; map<ll,ll> h; for (int i = 0; i < T; v2 = v1 = Mul(v1,a,p), i++) h[Mul(v1,b,p)] = i; for (int i = 1; i <= T + 2; v2 = Mul(v2,v1,p), i++) if ( h.count(v2) ) return i * T - h[v2]; return -1; }

Lucas定理#

如果p是质数,那么

(nm)(nmodpmmodp)×(npmp)(modp)

可以在模数较小时的求组合数。

Copy
inline int C(int n,int m) { return n < m ? 0 : mul( fac[n], mul( finv[m], finv[n-m] ) ); } inline int Lucas(int n,int m) { return n < p && m < p ? C(n,m) : mul( C(n%p,m%p), Lucas(n/p,m/p) ); }

素数判定和分解质因数#

MillerRabin测试#

首先我们可以用费马小定理来逆向测试,也就是随机一些a,判定ax1modx是否等于1,如果不是1,显然x是合数。

但是这样做仍然有些强伪素数会通过测试,也就是说一个数x现在满足对于随机的a,都有ax11(modx),现在我们要判定x是不是素数。

我们知道当p是素数的时候x21(modp)的解是x=1/x=p1,所以我们就把ax1开方,如果不是1/p1就可以判定它不是素数,如果是开方后是1的话可以继续判定。

这样做在long long范围内不会误判。

Copy
inline bool MillerRabin(int x) { if ( x == 2 || x == 3 || x == 5 || x == 7 ) return true; if ( x == 1 || x % 2 == 0 ) return false; int a = x - 1, b = 0; while ( not( a & 1 ) ) a>>=1, b++; for (int i = 1, j; i <= 8; i++) { int base = rand() % ( x - 2 ) + 2, v = fastpow(base,a,x); if ( v == 1 || v == x - 1 ) continue; for (j = 0; j < b; j++) if ( ( v = 1LL * v * v % x ) == x - 1 ) break; if ( j == b ) return false; } return true; }

Pollard-Rho分解质因数#

我们可以在[1,n]间随机x1,x2,,xkk个数字,然后试图判定gcd(|xixj|,n)>1,如果存在,那么我们就找到了一个n的非平凡质因子,根据生日悖论,其成功率很可观。

随机数可以用x1=c,xi=(x2i1+c)modn的方式生成,分布比较均匀,但是可能会出现ρ型环,需要特判。

一个很有效的判环策略是:令一个变量x以每次迭代两下的速度同时计算,如果出现x=x,那么就说明出现环,此时只要更改常数c的值重新计算即可。

这样做的期望复杂度是O(n14polylog(n))的,要配合Miller Rabin测试,基本不会误判。处理1018范围内的数据时,要注意配合快速乘。

Copy
inline ll PollardRho(ll x) { if ( MillerRabin(x) ) return x; for (ll c = 3, x1, x2, i, j; x1 = x2 = i = 1, j = 2; ++c) for (ll d; x1 = (Mul(x1,x1,x)+c) % x, d = gcd(abs(x1-x2),x);) { if ( 1 < d && d < x ) return max( PollardRho(d), PollardRho(x/d) ); if ( x1 == x2 ) break; if ( ++i == j ) j <<= 1, x2 = x1; } }

阶和原根#

阶的定义和性质#

gcd(a,p)=1,称方程ax1(modp)的最小正整数解xap的阶,记作δp(a)

性质1:对于任意的n满足an1(modp),都有δp(a)|n,证明如下:

若不成立,则一定有n=k×δp(a)+r(r<δp(a)),那么:anak×δp(a)×ar1(modp),所以ar1(modp),这与δp(a)的最小性矛盾,故假设不成立。

根据欧拉定理可得推论:δp(a)|φ(p)

性质2:设x=δp(a),则a0,a1,,ax1两两不同余,证明如下:

若不成立,不妨设0i<j<x满足aiaj(modp),那么aji1(modp),显然0<ji<x,与δp(a)的最小性矛盾,故假设不成立。

性质3:设x=δp(a),则δp(at)=xgcd(x,t),tN+,证明如下:

不妨设δp(at)=y,根据阶的定义aty1(modp),根据阶的性质1,有x|ty,可以导出xgcd(x,t)|tgcd(x,t)×y,于是xgcd(x,t)|y,根据阶的最小性,显然y=xgcd(x,t)

原根的定义和性质#

gcd(g,p)=1,δp(g)=φ(p),则称g为模p意义下的一个原根。

根据阶的性质和既约剩余系的乘法封闭性,我们还可以知道g为模p意义下的一个原根,当且仅当{¯g,¯g2,,¯gφ(p)}构成一个模p意义下的既约剩余系。

最小原根的求法:我们可以直接在2p1范围内枚举g,只要验证i[2,φ(p)1]gi1(modp)恒不成立即可。而根据推论,如果存在gi1(modp),一定有i|φ(p),所以只要枚举φ(p)的约数验证即可。再进一步,我们可以对于每个φ(p)的质因数piφ(p)pi来验证,因为他们涵盖了φ(p)所有真因子的倍数。

可以证明,一个正整数p的最小原根是O(n14)级别的,那么结合Pollard-ρ算法,可以实现O(p14log2p)求一个最小原根。

Copy
inline void PollardRho(ll x) { if ( MillerRabin(x) ) return fac.push_back(x), void(); for (ll c = 3, x1, x2, i, j; x1 = x2 = i = 1, j = 2; ++c) for (ll d; x1 = (Mul(x1,x1,x)+c) % x, d = gcd(abs(x1-x2),x);) { if ( 1 < d && d < x ) return PollardRho(d), PollardRho(x/d); if ( x1 == x2 ) break; if ( ++i == j ) j <<= 1, x2 = x1; } } inline ll Phi(ll p) { if ( MillerRabin(p) ) return p-1; ll val = p; PollardRho(p), sort( fac.begin(), fac.end() ); fac.erase( unique(fac.begin(),fac.end()), fac.end() ); for (int i = 0; i < fac.size(); i++) val /= fac[i], val *= (fac[i]-1); return fac.clear(), val; } inline ll PrimitiveRoot(ll p,ll val = 0) { if ( p == 2 ) return 1; PollardRho( val = Phi(p) ); sort( fac.begin(), fac.end() ); fac.erase( unique(fac.begin(),fac.end()), fac.end() ); for (ll i = 2, flag; flag = 1, i < p; i++) { for (int j = 0; flag && j < fac.size(); j++) if ( Pow(i,val/fac[j],p) == 1 ) flag = 0; if ( flag ) return fac.clear(), i; } }

原根存在判定定理:在模m意义下存在原根,当且仅当m=2,4,pk,2pk,其中p为质数,k为正整数。

求出一个数的最小原根g以后,我们可以进一步求出这个数的所有原根:集合G={gs|s[1,φ(p)],gcd(s,φ(p))=1}给出了模p意义下的所有原根,一共有φ(φ(p))个,证明如下:

根据原根的性质,集合G0={gs|s[1,φ(p)]}生成了模p意义下的既约剩余系,已经考察了所有可能的原根,所以只要说明gs是模p意义下的原根,一定满足gcd(s,φ(p))=1即可。根据阶的性质:δp(gs)=φ(p)gcd(φ(p),s),如果gs是原根,显然δp(gs)=φ(p),那么就有gcd(φ(p),s)=1,进而可知原根一共有φ(φ(p))个。

原根的应用#

假设g是模p意义下的原根,那么称方程gxa(modp)的最正小整数解为离散对数,记作indg(a)

离散对数具有和连续对数一样的性质:

indg(ab)indg(a)+indg(b)(modφ(p))indg(at)t×indg(a)(modφ(p))

离散对数可以在O(p)的时间内求解k次剩余,即方程xka(modp)的解:

xka(modp)k×indg(x)indg(a)(modφ(p))(gk)indg(x)gindg(a)a(modp)

处理出原根g之后,indg(x)的值就可以用BSGS算法求出,然后再快速幂还原出x即可。

不妨设x0gc(modp)是原问题的一个解,那么xkgkc+tφ(p)a(modp),显然解xgc+tφ(p)k(modp),参数t显然要满足kgcd(k,φ(p))|t,不妨设t=i×kgcd(k,φ(p)),iN,那么原问题的所有解为:xgc+φ(p)gcd(φ(p),k)×i(modp),iN

Copy
#include <bits/stdc++.h> using namespace std; typedef long long ll; vector<ll> fac; inline ll Mul(ll a,ll b,ll p) { return ( a * b - (ll)( (long double) a * b / p ) * p + p ) % p; } inline ll Pow(ll a,ll b,ll p) { ll c = 1; for (; b; a = Mul(a,a,p), b >>= 1) if (1&b) c = Mul(c,a,p); return c; } inline ll gcd(ll a,ll b) { return b == 0 ? a : gcd( b, a % b ); } inline ll lcm(ll a,ll b) { return a / gcd(a,b) * b; } inline bool MillerRabin(ll x) { if ( x == 2 || x == 3 || x == 5 || x == 7 ) return true; if ( x == 1 || x % 2 == 0 ) return false; ll a = x - 1, b = 0; while ( not( a & 1 ) ) a>>=1, ++b; for (int i = 1, j; i <= 8; i++) { ll Base = rand() % ( x - 2 ) + 2, v = Pow(Base,a,x); if ( v == 1 || v == x - 1 ) continue; for (j = 0; j < b; j++) if ( ( v = Mul(v,v,x) ) == x - 1 ) break; if ( j == b ) return false; } return true; } inline void PollardRho(ll x) { if ( MillerRabin(x) ) return fac.push_back(x), void(); for (ll c = 3, x1, x2, i, j; x1 = x2 = i = 1, j = 2; ++c) for (ll d; x1 = (Mul(x1,x1,x)+c) % x, d = gcd(abs(x1-x2),x);) { if ( 1 < d && d < x ) return PollardRho(d), PollardRho(x/d); if ( x1 == x2 ) break; if ( ++i == j ) j <<= 1, x2 = x1; } } inline ll Phi(ll x) { if ( MillerRabin(x) ) return x - 1; ll val = x; PollardRho(x), sort( fac.begin(), fac.end() ); fac.erase( unique(fac.begin(),fac.end()), fac.end() ); for (int i = 0; i < fac.size(); i++) val /= fac[i], val *= (fac[i]-1); return fac.clear(), val; } inline ll PrimitiveRoot(ll x,ll val = 0) { if ( x == 2 ) return 1; PollardRho( val = Phi(x) ); sort( fac.begin(), fac.end() ); fac.erase( unique(fac.begin(),fac.end()), fac.end() ); for (ll i = 2, flag; flag = 1, i < x; i++) { if ( gcd(i,x) != 1 ) continue; for (int j = 0; flag && j < fac.size(); j++) if ( Pow(i,val/fac[j],x) == 1 ) flag = 0; if ( flag ) return fac.clear(), i; } } inline ll BSGS(ll a,ll b,ll p) { ll T = sqrt(p), v1 = 1, v2; map<ll,ll> h; for (int i = 0; i < T; v2 = v1 = Mul(v1,a,p), i++) h[Mul(v1,b,p)] = i; for (int i = 1; i <= T + 1; v2 = Mul(v2,v1,p), i++) if ( h.count(v2) ) return i * T - h[v2]; return -1; } int main(void) { ll k,a,p,g,x0,phi,t; vector<ll> x; int T = 0; while ( ~scanf( "%lld%lld%lld", &k, &p, &a ) ) { printf( "case%d:\n", ++T ); g = PrimitiveRoot(p), x0 = BSGS(Pow(g,k,p),a,p), phi = Phi(p); if ( x0 == -1 ) { puts("-1"); continue; } t = phi / gcd(k,phi), x0 %= t, x.clear(); for (; x0 < phi; x0 += t) x.push_back(x0); for (int i = 0; i < x.size(); i++) x[i] = Pow(g,x[i],p); sort( x.begin(), x.end() ); for (int i = 0; i < x.size(); i++) printf( "%lld\n", x[i] ); } return 0; } // HDU3930

基础数论例题#

GCD Table#

假设符合要求的位置起点为(x,y),那么显然i[1,k],ai|x,也就是lcm(a1,a2,,ak)|x,显然x可以取k×lcm(a1,,ak),但是k>1还要求gcd(k,y+i1ai)=1,显然k>1有解,k=1也有解。

注意到我们还要求i[1,k],ai|(y+i1),所以i[1,k],y1i(modai),用ExCRT解这个同余方程组,注意要让y正整数

即使(x,y),,(x,y+k1)都落在给定的表格内,也要再次检验是否符合题意,因为保证下标都是对应ai的倍数,有可能gcd也是ai的倍数,不是ai

仪仗队#

2+n1i=1n1j=1e(gcd(i,j))=3+2n1i=1i1j=1e(gcd(i,j))=3+2n1i=1φ(i)

Longge的问题#

ni=1gcd(i,n)=d|ndndi=1e(gcd(i,nd))=d|ndφ(nd)

沙拉公主的困惑#

考虑到gcd(x,y)=gcd(x+y,y)=gcd(x+ky,y),所以对于i[1,m!],当一个gcd(i,m!)取到1时,总共会有n!m!gcd(i+k(m!),m!)取到1,所以:

n!i=1e(gcd(i,m!))=n!m!m!i=1e(gcd(i,m!))=n!m!φ(m!)

外星人#

首先要发现只有φ(2)=1,然后考虑唯一分解式取φ之后会发生什么:

φ(paii)=(pi1)pai1i

我们发现,每次取φ后原数中的因子2会因为pi=2的部分恰好减少一个,但是会被其他pi是奇质数的部分增加若干个。当这个数字n没有任何因子2的时候,n就是1了,所以取φ函数的次数,就恰好是n所有质因子的幂会产生的2的个数之和。

f(x)表示x会产生多少个因子2,显然f(p)=f(p1)f(a×p)=f(a)×f(p)(pPrime),这一过程在线性筛里面处理即可,那么根据输入就可以直接计算答案了。

要注意,当n一开始为奇数的时候,第一次取φ没有因子2可以消去,所以答案要+1

上帝与集合的正确用法#

2222(222)modφ(p)+φ(p)(modp)

直接递归,当φ(p)=1时,返回0即可。

数论函数#

符号及约定#

1. [P]是指正则表达式,当Ptrue时,[P]=1,当Pfalse时,[P]=0

2. 约数个数函数定义为τ(n)=d|n1

3. 约数和函数定义为σ(n)=d|nd

4. 元函数定义为e(n)=[n=1]

5. 恒等函数定义为I(n)=1

6. 单位函数定义为ϵk(n)=nk

7. 欧拉函数定义为φ(n)=ni=1[gcd(i,n)=1]

8.n=pcii,则莫比乌斯函数定义为μ(n)={1n=1(1)kci=10ci>1

9. 对于数论函数f,若满足gcd(a,b)=1时,有f(ab)=f(a)f(b),则称函数f积性函数

10. 对于两个函数f,g,定义他们的dirichlet卷积为:(f×g)(n)=d|nf(d)g(nd)函数f,g不必要是积性函数

欧拉筛法#

前置知识里已经提到过,以下给出本文可能会使用的线性筛代码:

Copy
int flag[N],Prime[N],cnt; inline void EularSieve(void) { for (int i = 2; i <= Lim; i++) { if ( !flag[i] ) Prime[++cnt] = i; for (int j = 1; j <= cnt && i * Prime[j] <= Lim; j++) { flag[ i * Prime[j] ] = true; if ( i % Prime[j] == 0 ) break; } } }

这是最朴素的欧拉筛法,不过当我们要筛一些比较复杂的积性函数的时候,线性筛方程的推导可能会非常复杂。

因此我们考虑引入一种更好的线性筛法,目的是能够方便的筛出积性函数的值。

首先,我们注意到积性函数的性质:当gcd(a,b)=1时,有f(ab)=f(a)f(b)。那么对于一个任意的正整数n,我们可以用算术基本定理进行分解:

n=ki=1pcii

此时,任意的i,j[1,k]都满足gcd(pcii,pcjj)=1。那么我们就可以得到:

f(n)=f(ki=1pcii)=ki=1f(pcii)

那么我们就有一个想法:利用定义求出积性函数f在所有素数幂出的取值,然后直接递推出所有函数值

具体地说,可以这样递推:

Copy
f[1] = 1; for (int i = 2; i <= Lim; i++) if ( i == Pow[i] ) f[i] = Calc(p[i],e[i]); else f[i] = f[i/Pow[i]] * f[Pow[i]];

其中pi代表数i的最小素因子,ei代表i的分解式中pi这个素因子的指数,Calc就是求素数幂处取值的函数,而Powi=peii,这样是不是就符合我们的要求了呢?

那么现在我们的问题就是如何求出p,e,Pow这三个数组,幸运的是,他们都可以在线性筛的过程中求。

根据欧拉筛法每次用一个数的最小素因子筛去这个合数,就可以更新这三个数组的值了。

代码如下:

Copy
int p[N],e[N],Pow[N],Prime[N],cnt; inline void EularSieve(void) { Pow[1] = p[1] = f[1] = 1 , e[1] = 0; for (int i = 2; i <= Lim; i++) { if ( p[i] == 0 ) p[i] = Pow[i] = Prime[++cnt] = i , e[i] = 1; for (int j = 1; j <= cnt && i * Prime[j] <= Lim; j++) { int Next = i * Prime[j]; p[Next] = Prime[j]; if ( p[i] == Prime[j] ) { e[Next] = e[i] + 1; Pow[Next] = Pow[i] * Prime[j]; break; } else e[Next] = 1 , Pow[Next] = Prime[j]; } } for (int i = 2; i <= Lim; i++) if ( i == Pow[i] ) f[i] = Calc(p[i],e[i]); else f[i] = f[i/Pow[i]] * f[Pow[i]]; }

Dirichlet卷积#

Dirichlet卷积的性质#

1. 两个积性函数fgdirichlet卷积仍为积性函数。

证明:

设有两个积性函数fg,则它们的dirichlet卷积为:

h=f×g=d|nf(d)g(nd)

对于函数h则可以得到:

h(x)h(y)=(d1|xf(d1)g(xd1))(d2|yf(d2)g(yd2)) =d1|x,d2|yf(d1d2)g(xyd1d2)=d|xyf(d)g(xyd)=h(xy)

故函数h为积性函数。

2. dirichlet卷积满足交换律。

证明:

设有数论函数fg,则有:

f×g=d|nf(n)g(nd)=d|ng(n)f(nd)=g×f

3. dirichlet卷积满足结合律。

证明:

设有数论函数fgh,则有:

(f×g)×h=f×g×h=g×h×f=(g×h)×f=f×(g×h)

4. dirichlet卷积满足分配律。

证明:

设有数论函数fgh,则有:

(g+h)×f=d|n(g(d)+h(d))f(nd)=d|ng(d)f(nd)+d|nh(d)f(nd)=g×f+h×f

常见的Dirichlet卷积#

1. f×e=f

正确性显然,对于任意函数f成立.

2. ϵ=φ×I

ϵ=e×ϵ=ϵ×μ×I=φ×I

3. τ=I×I

I2(n)=d|nI(d)I(nd)=d|n1=τ(n)

4. σ=ϵ×I

(ϵ×I)(n)=d|nϵ(d)I(nd)=d|nd=σ(n)

5. e=μ×I

(μ×I)(n)=d|nμ(d)I(dn)=d|nμ(d)

不妨设n=ki=1paiiP={1,p1,p2,,pk1},因为当x有平方因子时μ(x)=0,所以可以进行如下转化:

(μ×I)(n)=d|nμ(d)=SP(μ(pSp)+μ(pkpSp))

对于质数p和正整数a满足p|a,显然有μ(p)+μ(ap)=0,所以:

(μ×I)(n)=d|nμ(d)=[n=1]=e(n)

6. \varphi=\epsilon\times \mu

(\epsilon \times \mu)(n)=\sum_{d|n}\mu(d)\frac{n}{d}=\varphi(n)

可以用欧拉函数的容斥计算来理解.

7. \sigma=\tau \times \varphi

\sigma =I\times \epsilon=I^2\times \varphi=\tau\times \varphi

下取整函数的性质#

1. 对于i\in \left [x, \left \lfloor \frac{k}{ \left\lfloor \frac{k}{x}\right \rfloor } \right \rfloor \right ]\left\lfloor \frac{k}{i} \right\rfloor的值都相等。

证明:

f(x)= \left \lfloor \frac{k}{ \left\lfloor \frac{k}{x} \right\rfloor } \right \rfloor,显然有f(x)\geq \left \lfloor \frac{k}{ \left( \frac{k}{x} \right) } \right \rfloor=x,则可得\left \lfloor \frac{k}{f(x)} \right \rfloor\leq \left \lfloor \frac{k}{x} \right \rfloor

从另一方面考虑,则有\left \lfloor \frac{k}{f(x)} \right \rfloor\geq\left \lfloor \frac{k}{ \frac{k}{\left \lfloor k/x \right \rfloor } } \right \rfloor=\left\lfloor \frac{k}{x}\right \rfloor,则可得:\left \lfloor \frac{k}{f(x)} \right \rfloor=\left \lfloor \frac{k}{x} \right \rfloor

2. \left\lfloor\frac{n}{k}\right\rfloor最多只有2\sqrt n种取值。

证明:

k\leq\sqrt n时,至多只有\sqrt nk,所以对应的取值只有\sqrt n种。

k>\sqrt n时,有1\leq\left\lfloor\frac{n}{k}\right\rfloor\leq\sqrt n,所以对应的取值也只有\sqrt n种。

那么当我们要对某个有关下取整函数的值求和的时候,就可以使用如下的整除分块算法,时间复杂度O(\sqrt n)

Copy
inline int Calc(void) { int res = 0; for (int l = 1, r; l <= n; l = r + 1) ( r = n/l ? min( n/(n/l) , n ) : n ), res += f(n/l); return res; }

对于二元的情况,其实也是一样的,我们考虑间断点合并,就可以证明其时间复杂度为O(\sqrt n + \sqrt m)

Copy
inline int Calclim(int n,int k,int l) { return k/l ? min( k/(k/l) , n ) : n; } inline int Calc(void) { int res = 0; for (int l = 1, r; l <= min(n,m); l = r + 1) r = min( Calclim( min(n,m), n, l ), Calclim( min(n,m), m, l ) ), // 此时,区间[l,r]中所有的 [n/l] 都相等,所有的 [m/l] 也都相等 res += f( n/l , m/l ); return res; }

3.m是正整数,则有\left\lfloor\frac{\lfloor x \rfloor}{m}\right\rfloor=\left\lfloor\frac{x}{m}\right\rfloor

证明:

x=km+r(r<m),则

\left\lfloor\frac{\lfloor x \rfloor}{m}\right\rfloor=\left\lfloor k+ \frac{\lfloor r \rfloor}{m}\right\rfloor=k\\ \ \\ \left\lfloor\frac{x}{m}\right\rfloor=\left\lfloor k+ \frac{r}{m}\right\rfloor=k

推论

\left\lfloor\frac{\left\lfloor k/n \right\rfloor}{m}\right\rfloor=\left\lfloor\frac{k}{nm}\right\rfloor

反演原理#

一般来说会用到两种反演原理:

\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]=\sum_{i=1}^n\sum_{j=1}^me(\gcd(i,j))=\sum_{i=1}^n\sum_{j=1}^m\sum_{k|\gcd(i,j)}\mu(k) \\

第一个,本质上是\mu\times I=e,用于一般的莫比乌斯反演.

F(n)=\sum_{d|n}f(d)\Longleftrightarrow f(n)=\sum_{d|n}g(d)\mu\left(\frac{n}{d}\right)

第二个,本质上是F=f\times I\Leftrightarrow f=F\times \mu,又称为因数形式莫比乌斯定理(倍数形式莫比乌斯定理基本上可以用反演原理一代替,这里不提了).

F(n)=\sum_{d|n}f(d)\Longleftrightarrow f(n)=F(n)- \sum_{d|n\and d\not = n}f(d)

第三个,没什么好说的,就是直接根据定义变形.

数论函数例题:几种常见的反演#

NOI2010 能量采集#

n,m\leq 10^5,求:

\sum_{i=1}^n\sum_{j=1}^m\left( 2\gcd(i,j)-1\right)

\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)=\sum_{d=1}^{\min(n,m)}d\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=d] \\ = \sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}[\gcd(i,j)=1] \\ = \sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}\sum_{k|\gcd(i,j)}\mu(k) \\ = \sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}\mu(k) \sum_{k|j}^{\lfloor n/d\rfloor}\sum_{k|j}^{\lfloor m/d\rfloor}1 \\ =\sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}\mu(k)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor \\ = \sum_{T=1}^{\min(n,m)}\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor \sum_{d|T}d\times \mu\left(\frac{T}{d} \right) \\ = \sum_{T=1}^{\min(n,m)}\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\varphi(T)

显然\varphi函数可以线性筛,那么时间复杂度就优化到O(\sqrt n+\sqrt m)回答每一组询问.

看到\gcd,其实可以用欧拉反演秒杀:

\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)=\sum_{i=1}^n\sum_{j=1}^m\sum_{k|\gcd(i,j)}\varphi(k) = \sum_{k=1}^{\min(n,m)}\varphi(k)\left\lfloor\frac{n}{k}\right\rfloor\left\lfloor\frac{m}{k}\right\rfloor

Luogu2257 YY的gcd#

T=10^4n,m\leq 10^7,求:

\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)\ \mathrm{is\ a \ prime\ number}]

\mathrm{LHS}=\sum_{p\ \mathrm{is\ prime\ number}}\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p] \\ \sum_{p}\sum_{i=1}^{\lfloor n/p\rfloor}\sum_{j=1}^{\lfloor m/p\rfloor}[\gcd(i,j)=1]=\sum_{p}\sum_{i=1}^{\lfloor n/p\rfloor}\sum_{j=1}^{\lfloor m/p\rfloor}\sum_{k|\gcd(i,j)}\mu(k) \\ = \sum_{p} \sum_{k=1}^{\min(\lfloor n/p\rfloor,\lfloor m/p\rfloor)}\mu(k)\sum_{k|i}^{\lfloor n/p\rfloor}\sum_{k|j}^{\lfloor m/p\rfloor}1 \\ = \sum_{p} \sum_{k=1}^{\min(\lfloor n/p\rfloor,\lfloor m/p\rfloor)}\mu(k)\left\lfloor\frac{n}{kp}\right\rfloor\left\lfloor\frac{m}{kp}\right\rfloor \\ =\sum_{T=1}^{\min(n,m)}\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{p\mathrm{\ is\ a\ prime\ number},p|T}\mu\left(\frac{T}{p}\right)

显然我们可以令

f(T)=\sum_{p\mathrm{\ is\ a\ prime\ number},p|T}\mu\left(\frac{T}{p}\right)

我们完全可以枚举每一个质数p然后调和级数地枚举其倍数,累加贡献,时间复杂度是O(\frac{n}{\ln n}\times \ln n)\approx O(n),当然也可以分类讨论线性筛,不过没什么必要.

BZOJ2693 jzptab#

T\leq 10^4n,m\leq 10^7,求:

\sum_{i=1}^n\sum_{j=1}^m\operatorname{lcm}(i,j)

\mathrm{LHS}=\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{\gcd(i,j)}=\sum_{d=1}^{\min(n,m)}\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{d}[\gcd(i,j)=d] \\ = \sum_{d=1}^{\min(n,m)}\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}dij[\gcd(i,j)=1]=\sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}ij[\gcd(i,j)=1] \\

F(n,m)=\sum_{i=1}^n\sum_{j=1}^mij[\gcd(i,j)=1],则:

F(n,m)=\sum_{i=1}^n\sum_{j=1}^mij\sum_{k|\gcd(i,j)}\mu(k)=\sum_{k=1}^{\min(n,m)}\mu(k)\sum_{k|i}^{n}\sum_{k|j}^mij \\ = \sum _{k=1}^{\min(n,m)}\mu(k)k^2\sum_{i=1}^{\lfloor n/k\rfloor}\sum_{j=1}^{\lfloor m/k\rfloor}ij

这里记S(n,m)=\sum_{i=1}^n\sum_{j=1}^mij=\frac{nm(n+1)(m+1)}{4},那么:

F(n,m)=\sum _{k=1}^{\min(n,m)}\mu(k)k^2S\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right) \\ \mathrm{LHS}=\sum_{d=1}^{\min(n,m)}d\sum_{k=1}^{\min(\lfloor n/d\rfloor,\lfloor m/d\rfloor)}\mu(k)k^2S\left(\left\lfloor\frac{n}{kd}\right\rfloor,\left\lfloor\frac{m}{kd}\right\rfloor\right) \\ =\sum_{T=1}^{\min(n,m)}S\left(\left\lfloor\frac{n}{T}\right\rfloor,\left\lfloor\frac{m}{T}\right\rfloor\right)\sum_{k|T}T\times k\mu(k)

显然后面的函数又是积性函数,可以线性筛,那么就可以O(\sqrt n+\sqrt m)回答一组询问了.

posted @   Parsnip  阅读(500)  评论(0编辑  收藏  举报
编辑推荐:
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
阅读排行:
· 《HelloGitHub》第 108 期
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单
· C# 13 中的新增功能实操
· Supergateway:MCP服务器的远程调试与集成工具
点击右上角即可分享
微信分享提示
目录