数♀论总♂结
太容易忘记数论的定理了,(也许是比较鶸),所以开了个坑来记一下定理.
1.最大公因数,最小公倍数
gcd(a,b)=gcd(b,a%b) (最大公因数)
lcm(a,b)=a*b/gcd(a,b) (最小公倍数)
2.莫比乌斯反演
μ(x)={ 1 (x==1)
(-1)^k (x是由k个互不相同的质数相乘)
0 (其他情况)}
(莫比乌斯太难了,以后懂了记得填坑)
筛法求莫比乌斯函数
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 const int maxn = 60000+5; 5 bool vis[maxn]; 6 int prime[maxn],primes,mu[maxn]; 7 void init_mu() 8 { 9 memset(vis,0,sizeof(vis)); 10 mu[1]=1; 11 primes=0; 12 for(int i=2; i<maxn; i++) 13 { 14 if(!vis[i]){ 15 prime[primes++]=i; 16 mu[i]=-1; 17 } 18 for(int j=0; j<primes&&i*prime[j]<maxn; j++) 19 { 20 vis[i*prime[j]]=1; 21 if(i%prime[j]) mu[i*prime[j]]=-mu[i]; 22 else { mu[i*prime[j]]=0;break;} 23 } 24 } 25 }
-2016-06-20 终于看懂一点点。。。,
这玩意常常用来求gcd的关系。
f(i)是i=gcd(x,y),g(i)是i|gcd(x,y)
那么f(n)=d|n u(n/d)*g(d)
如果有式子满足f(n)=Σ d|n a(d)*b(n/d)
我们通常表示为f=a*b,这是卷积的表示,其中,如果f(n)=Σ d|n a(d),那么我们表示为f=a*1,
1实际上代表单位卷积。1*1代表的是除数函数中x等于0的情况:
卷积满足交换律和结合律。。
-2016-06-20
3.欧拉函数
欧拉函数属于积性函数(gcd(a,b)==1,f(a*b)=f(a)*f(b)),不是完全积性函数(任何情况 f(a*b)=f(a)*f(b))
若x为质数 phi(x)=x-1;
若x为奇数 phi(2*x)=phi(x)*2
若x为p^k,p为质数 phi(x)=x-x/p=p^k-p^(k-1)=(p-1)*p^(k-1) (因为除了p的倍数以外,都和x互质)
若a,b互质,b为质数,phi(a*b)=phi(a)*(b-1)
欧拉定理:若a,n互质 则有 a^(phi(n))= 1 (mod n)
O(n)筛法:
1 void getphi() 2 { 3 phi[1]=1; 4 for(int i=2;i<=n;i++) 5 { 6 if(!mark[i]){phi[i]=i-1;pri[++tot]=i;} 7 for(int j=1;j<=tot;j++) 8 { 9 int x=pri[j]; 10 if(i*x>n)break; 11 mark[i*x]=1; 12 if(i%x==0){phi[i*x]=phi[i]*x;break;} 13 else phi[i*x]=phi[i]*phi[x]; 14 } 15 } 16 }
1 //直接求解欧拉函数 2 int euler(int n){ //返回euler(n) 3 int res=n,a=n; 4 for(int i=2;i*i<=a;i++){ 5 if(a%i==0){ 6 res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出 7 while(a%i==0) a/=i; 8 } 9 } 10 if(a>1) res=res/a*(a-1); 11 return res; 12 }
4.exgcd
已知a,b求解x,y满足a*x+b*y=gcd(a,b)
若a==1,b==0 则x=1,y=0;
若已知下一层x,y
则当前t=x x=y y=t-a/b*y;
由于gcd(a,b)=gcd(b,a%b)
则有ax+by=bx+(a%b)y
ax+by=bx+(a-a/b*b)y
ax+by=ay+b*(x-a/b*y)
注意:如求某数N的逆元,则令a=N,b=p(假如是在模p下),如果令b=0会错误.
EXGCD求线性方程组:
5.原根
如果模p有原根,那么它一定有 phi(phi(p))个原根。
若有(g^i )%p(0<i<=p-1) 两两不同,则称g为p原根。
若p为素数,p一定有原根。g^(P-1) = 1 (mod P)(p为素数)
m有原根的充要条件:m=2,4,2*p^a,p^a 其中p为奇素数
原根通常很小,往往暴力求
求模素数p的原根:
对p-1质因数分解,分解成 p1^k1*p2^k2*p3^k3...pn^kn=p-1
对于一个数g若恒有 g^((p-1)/pi)!=1 (mod p)则g就是p的原根。
对合数p的原根:
讲上述p-1换成phi(p)即可。
6.逆♂元
若有a*x==1 (mod p),则称x是a在模p下的逆元
逆元运用广泛,可以进行转换等
6.1
(若a,p互质)a^phi(p)==1 (mod p) (费马小定理)
即有 a^(phi(p)-1)== a^(-1) (mod p)
即若p为质数,a在p下的逆元即是 (a^(p-2))%p;
6.2
若a/b中有b|a,现求a/b (mod p)
a/b=k*p+x (x即为a/b (mod p))
a=k*p*b+b*x
a%pb=b*x
x=(a%pb)/b
6.3
有些题目需要用到1->M 模M的所有逆元,我们有一个非常好♂用的顺推公式
inv[i]=(M-M/i)*inv[M%i]%M
证明如下:
设t=M/i,k=M%i
i*t+k=0 (mod M)
-i*t==k(mod M) 两边同时除以i*k得到
-t*inv[k]==inv[i](mod M)
inv[i]=-t*inv[k]将t=M/i,k=M%i带入可得上式
6.4
ax==b(mod p)
求最小解x
首先若gcd(a,p)不是b的因数则无解,否则将a,b带入exgcd(a,b,x,y)算出其中x即是满足上式的x
6.5
求阶乘的逆元?若p为素数
可以先让f[n]=((!n)%p)^(p-2)%p
然后从n-1往1:f[i]=f[i+1]*i%p就行辣!
简单来说就是先求 总阶乘的逆元,然后倒着乘回去
7.二次剩余
二次剩♂余:x^2== a(mod p)
有解则a为p的二次剩余,否则a是p的二次非剩余
若 a^((p-1)/2)==1(mod p)a是p的二次剩余
8.指标
指标:定义指标为I(A),那么I(A)就是最小令g^I(A)==A (mod p) 其中g是p的原根,若有x^a==b(mod c)则有 a*I(x)==I(b)(Mod(c-1)) PS:通常我们计算出原根基本都会用再预处理出指标
9.裴蜀定理
ax+by=gcd(a,b)一定有解
10.求幂大法和降幂大法
a^b Mod B=A^(b Mod phi(B)+phi(B)) %Mod B (其中b>=phi(B)) 求幂大法
p为素数 X^a%p=X^(a%(p-1))%p 降幂大法
11.组合数
运用广泛
11.1
杨辉三角:C[i][j]=C[i-1][j-1]+C[i-1][j] 计算时间O(N^2)
11.2
lucas定理 用于求C(n,m)%p, p为素数
Lucas(n,m)=C(n%p,m%p)*Lucas(n/p,m/p)
1 typedef long long LL; 2 using namespace std; 3 4 LL exp_mod(LL a, LL b, LL p) { 5 LL res = 1; 6 while(b != 0) { 7 if(b&1) res = (res * a) % p; 8 a = (a*a) % p; 9 b >>= 1; 10 } 11 return res; 12 } 13 14 LL Comb(LL a, LL b, LL p) { 15 if(a < b) return 0; 16 if(a == b) return 1; 17 if(b > a - b) b = a - b; 18 19 LL ans = 1, ca = 1, cb = 1; 20 for(LL i = 0; i < b; ++i) { 21 ca = (ca * (a - i))%p; 22 cb = (cb * (b - i))%p; 23 } 24 ans = (ca*exp_mod(cb, p - 2, p)) % p; 25 return ans; 26 } 27 28 LL Lucas(int n, int m, int p) { 29 LL ans = 1; 30 31 while(n&&m&&ans) { 32 ans = (ans*Comb(n%p, m%p, p)) % p; 33 n /= p; 34 m /= p; 35 } 36 return ans; 37 } 38 39 int main() { 40 Read(); 41 int n, m, p; 42 while(~scanf("%d%d%d", &n, &m, &p)) { 43 printf("%lld\n", Lucas(n, m, p)); 44 } 45 return 0; 46 }
11.3
组合数取模?
C(n,m)=!n/(!(n-m)*!(m))
因此我们可以使用6.5中求阶乘逆元的方式,求出!(n-m)和!(m)在模下的逆元,然后相乘再乘上!n模模数就是答案!
12.BSGS (拔山盖世(雾))
a^x==b(mod p)
令x=i*m+j,其中m=ceil(sqrt(p)
a^(i*m+j)==b(mod p)
那么有
a^(j)==a^(-i*m)*b(mod p)其中a^(-i*m)就是a^(i*m)在模P下的逆元
那么我们只要枚举j,用哈希表存下 a^j %p,然后再枚举a^(-i*m)*b (mod p),在哈希表下查找就可以了
至于为什么要这样找和为什么m=ceil(sqrt(p)),我是不懂的。
13.质数
13.1 大于等于sqrt(n)的n的质因子不会超过一个
13.2 在!n内质因子p的出现个数为 n/p+n/(p^2)+n/(p^3)...+n/(p^k) (p^(k+1)刚好大于n)
13.3 筛法求质数
1 typedef long long LL; 2 #define mn 100000+5 3 LL ph[mn]; 4 bool vis[mn]; 5 int primes, prime[mn]; 6 void Init() 7 { 8 ph[1] = 1; 9 primes=0; 10 for (LL i = 2; i < mn; ++i) 11 { 12 if (!vis[i]) 13 { 14 prime[primes++] = i; 15 ph[i] = i-1; 16 } 17 for (LL j = 0; j < primes && i*prime[j] < mn; ++j) 18 { 19 vis[i*prime[j]] = true; 20 if (i % prime[j]) 21 ph[i*prime[j]] = ph[i]*(prime[j]-1); 22 else 23 { 24 ph[i*prime[j]] = ph[i]*prime[j]; 25 break; 26 } 27 } 28 } 29 }
这个算法的关键在于 if(i%pr[j] == 0) break;。它使得任何一个合数,只被它最小的质因数标记过一次