数论模板总结

(1)模板表头

//所有模板默认 prime[],powe[]从下标0开始取
const int MAXL = 1e6+7;
int
phi[MAXL];//欧拉函数 int tot; int prim[MAXL];//素数表 int cnt; int vis[MAXL];
int powe[MAXL];//质数
int cur;

(1*)全部头文件

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <complex>
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define mp make_pair
#define Accept 0
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double Pi = acos(-1.0);
const double esp = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+7;
const int maxm = 1e6+7;
const int mod = 1e9+7;
//所有模板默认 prime[],powe[]从下标0开始取
const int MAXL = 1e6+7;
int phi[MAXL];//欧拉函数
int tot;
int prim[MAXL];//素数表
int cnt;
int vis[MAXL];
int powe[MAXL];//质数幂
int cur;
View Code

(1)素数筛
<1>暴力判断:O(n*sqrt(n))

bool isprime(int n)
{
    int i;
    for(i=2;i<=sqrt(n);i++)
        if(n%i==0)
            return false;
    return true;
}
View Code

<2>埃拉托斯特尼筛O(nlog^logn)

int vis[MAXL];
int prim[MAXL];
//返回打出的素数个数
int getPrime(){
    memset(vis,0,sizeof(vis));
    memset(prim,0,sizeof(prim));
    int cnt=0;
    //打出MAXL 以内的素数表
    vis[0]=vis[1]=1;
    int m = sqrt(MAXL);
    for(int i = 2; i < m ;i++) if(!vis[i])
           for(int j = i*i; j < MAXL;j+=i)
        vis[j] = 1;
    for(int k=0;k<MAXL;k++)
        if(!vis[k]) prim[cnt++]=k;
    return cnt;
}
View Code

<3>欧拉筛(线性)O(N)

int getPrime(){
    int i ,j;
    int cnt = 0;
    memset(vis,0,sizeof(0));
    vis[0] = vis[1] = 1;
    for(i=2;i<=MAXL;++i)
    {
        if(!vis[i]) vis[i]= prim[cnt++]= i;
        for(j=0;j<cnt&&i*prim[j]<=MAXL;++j){
            vis[i*prim[j]] = 1;
            if(i%prim[j]==0) break;
        }
    }
    return cnt;
}
View Code

(1)唯一分解定理(求分解数组powe)

int getPower(ll n,int cnt){
    memset(powe,0,sizeof(powe));//cnt为prime最大下标(以免越界)
    int cur =  0;//cur的下标与素数表的下标相同 
    for(int i = 0; i < cnt ; i++)//i为素数的下表
    {
        if(n==1) break;
        while(n%prim[i]==0){
            powe[cur]++;
            n/=prim[i];
        }
        cur++;
    }return cur;//powe最大素数下标
}
View Code

(1*)唯一分解定理常用(优化)

int getPower(ll n,int cnt){
    //直接用下标表示质因子值
    memset(powe,0,sizeof(powe));//cnt为prime最大下标(以免越界)
    int cur = 0;
    for(int i = 0; (ll)prim[i]*prim[i]<=n && i<cnt ; i++)
    {
        while(n%prim[i]==0){
            powe[prim[i]]++;
            n /= prim[i];
        }
        cur = prim[i];
    }
    if(n>1) {
        powe[n]++;
        return n;//返回最大质因子下标
    }else {
        return cur;
    }
}
View Code

(2)欧拉函数

<1>单个求

int getPhi(int x){
    int ans = x;
    for(int i = 2; i*i <= x; i++){
        if(x % i == 0){
            ans = ans / i * (i-1);
            while(x % i == 0) x /= i;
        }
    }
    if(x > 1) ans = ans / x * (x-1);
    return ans;
}
View Code

<2>线性打表+(打prime表)

void eulerPhi(){
    tot = 0;
    memset(phi,0,sizeof(phi));
    phi[1] = 1;
    for(int i = 2; i < MAXL; i ++){
        if(!phi[i]){
            phi[i] = i-1;
            prim[tot ++] = i;
        }
        for(int j = 0; j < tot && 1ll*i*prim[j] < MAXL; j ++){
            if(i % prim[j]) phi[i * prim[j]] = phi[i] * (prim[j]-1);
            else{
                phi[i * prim[j]] = phi[i] * prim[j];
                break;
            }
        }
    }
}
View Code

(3)快速幂

ll quickPow(ll a, ll b){
    int ans = 1;
    a = a % mod;
    while(b){
        if(b&1)ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans%mod;
}
View Code

(4)GCD

<0>gcd(a,b) 与 lcm(a,b) 与唯一分解定理

已知
a=p1^n1 * p2^n2 * p3^n3...
b=p1^m1 * p2^m2 * p3^m3...
(a,b)=p1^min(n1,m1) * p2^min(n2,m2) * p3^min(n3,m3)...
[a,b]=p1^max(n1,m1) * p2^max(n2,m2) * p3^max(n3,m3)...
View Code

<0>gcd 与 lcm

lcm = gcd *  (a / gcd) * (b / gcd)

<1>gcd

ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
View Code

<2>exgcd(最小正整数解,求逆元)

下面两式子相等
ax+ by= c 方程最小正整数解( p=exgcd(a,b),if(c%p==0)有解 else 无解; )
ax≡b(mod n) 求逆元原理(同余方程)
ax+by=gcd(a,b) (a,b互质才有解)//该模板
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b,a%b,x,y),temp = x;
    x = y;
    y = temp-a/b*y;
    return d;
}
View Code

(5)约数

<1>约数个数

const int N=1e5+5;
bool mark[N];
int prim[N],d[N],num[N];
int cnt;
void getFactorNum()
{
    cnt=0;
    d[1]=1;
    for (int i=2 ; i<N ; ++i)
    {
        if (!mark[i])
        {
            prim[cnt++]=i;
            num[i]=1;
            d[i]=2;
        }
        for (int j=0 ; j<cnt && i*prim[j]<N ; ++j)
        {
            mark[i*prim[j]]=1;
            if (!(i%prim[j]))
            {
                num[i*prim[j]]=num[i]+1;
                d[i*prim[j]]=d[i]/(num[i]+1)*(num[i*prim[j]]+1);
                break;
            }
            d[i*prim[j]]=d[i]*d[prim[j]];
            num[i*prim[j]]=1;
        }
    }
}
View Code

 <2>约数和

/*
约数和
sd[i] 表示 i 的约数和
sp[i] 表示 i 的最小素因子的等比数列的和
(我不知道怎么形容这个啊,就上面说要保存的那一项)
prim[i] 表示第 i 个素数
*/
const int N=1e5+5; 线性塞打表约数和 
bool mark[N];
int prim[N];
long long sd[N],sp[N];
int cnt;//prim个数
void primeSum()
{
    cnt=0;
    sd[1]=1;
    for (int i=2 ; i<N ; ++i)
    {
        if (!mark[i])
        {
            prim[cnt++]=i;
            sd[i]=i+1;
            sp[i]=i+1;
        }
        for (int j=0 ; j<cnt && i*prim[j]<N ; ++j)
        {
            mark[i*prim[j]]=1;
            if (!(i%prim[j]))
            {
                sp[i*prim[j]]=sp[i]*prim[j]+1;
                sd[i*prim[j]]=sd[i]/sp[i]*sp[i*prim[j]];
                break;
            }
            sd[i*prim[j]]=sd[i]*sd[prim[j]];
            sp[i*prim[j]]=1+prim[j];
        }
    }
}
View Code

(6)模运算

运算规则:
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
(a ^ b) % p = ((a % p) ^ b ) % p
模运算的结合律:
((a + b) % p + c) % p= (a + (b + c) % p) % p
((a * b) % p * c) % p = (a * (b * c) % p ) % p
交换律:
(a + b) % p = (b+a) % p
(a * b) % p = (b * a) % p
分配率:
((a +b) % p * c) % p = ((a * c) % p + (b * c) % p) % p
View Code

(7)组合数

<1>Lucas

/*
lucas定理(求较大组合数 ,在除数时采用逆元运算
公式:C(n, m) % p  =  C(n / p, m / p) * C(n%p, m%p) % p
对于C(n / p, m / p),如果n / p 还是很大,可以递归下去
*/
ll fac[mod+105],inv[mod+105];
void getInv()
{
    fac[0]=fac[1]=inv[1]=1;
    for(int i=2;i<mod;i++)
    {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
}
ll comb(int n, int m, ll p){//C(n, m) % p 组合数公式
    if (m < 0 || m > n) return 0;
  return fact(n, p) * inv(fact(m, p), p) % p * inv(fact(n-m, p), p) % p;
}
ll Lucas(ll n,ll m)//Lucas对组合数的处理(化简
{
    if(m>n)return 0;
    if(n<mod && m<mod)return C(n,m);
    return Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
ll fact(){
    
}
View Code

<1>*扩展Lucas

(8)逆元

<0>逆元取模运算规则

(a  /  b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p

<1>乘法逆元(打表)

1.乘法逆元定义:// a和p互质,a才有关于p的逆元
如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x。

打表:

//逆元打表:原理;inv(a) = (p - p / a) * inv(p % a) % p
int inv[MAXL];//求1-MAXL关于MOD的逆元
int init(){
    inv[1] = 1;
    for(int i = 2; i < MAXL; i ++){
        inv[i] = (mod - mod / i) * 1ll * inv[mod % i] % mod;
    }
}

<2>费马小定理

//费马小定理:快速幂模板的 特别参数
//假如a是一个整数,p是一个质数,那么 是p的倍数//(注意a,p条件)
//A * (a^(p-1)-1) =n*p ->   如果a不为p的倍数 ->
LL Fermat(LL a, LL p){//费马求a关于b的逆元 return pow_mod(a, p-2, p); }

<3>扩展欧几里得求逆元

//扩展欧几里得:a*x + b*y = 1 当a,b互质时有解,x为a关于b的逆元
LL inv(LL t, LL p){//如果不存在,返回-1 
    LL d, x, y;
    ex_gcd(t, p, x, y, d);
    return d == 1 ? (x % p + p) % p : -1;
}

(7)中国剩余定理(同余方程)

 
求解原理:通过转化为对应的逆元求解式求解

互质情况:假设整数m1,m2, ... ,mn两两互质,则对任意的整数:a1,a2, ... ,an, 
方程组(S)有解
//n个方程:x=a[i](mod m[i]) (0<=i<n)
LL china(int n, LL *a, LL *m){
    LL M = 1, ret = 0;
    for(int i = 0; i < n; i ++) M *= m[i];
    for(int i = 0; i < n; i ++){
        LL w = M / m[i];
        ret = (ret + w * inv(w, m[i]) * a[i]) % M;
    }
    return (ret + M) % M;
}
对于不互质的情况:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
LL a[100000], b[100000], m[100000];//对应的线性方程输入数组

PLL linear(LL A[], LL B[], LL M[], int n) {//求解(A[i])x = B[i] (mod M[i]),总共n个线性方程组 
    LL x = 0, m = 1;
    for(int i = 0; i < n; i ++) {
        LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a);
        if(b % d != 0)  return PLL(0, -1);//答案不存在,返回-1 
        LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d);
        x = x + m*t;
        m *= M[i]/d;
    }
    x = (x % m + m ) % m;
    return PLL(x, m);//返回的x就是答案,m是最后的lcm值 
}
View Code

阶乘位数计算:

阶乘位数:
数据域:int 2^32 long long 64
     #include<stdio.h>  
    #include<math.h>  
    void main()  
    {  
      int n,i;  
      double d;  
      scanf("%d",&n);  
      d=0;  
      for(i=1;i<=n;i++)  
          d+=log10(i);  
      printf("%d
",(int)d+1);  
    }  
32位以内:29!~30!
斯特林公式求阶乘位数(优化:
    #include<stdio.h>  
    #include<math.h>  
    #define PI 3.1415926  
    int main()  
    {  
    int cases,n,ans;  
    scanf("%d",&cases);  
    while(cases--)  
    {  
       scanf("%d",&n);  
       if(n==1)  
        printf("1
");  
       else  
       {  
         ans=ceil((n*log(n)-n+log(2*n*PI)/2)/log(10));  
          printf("%d
",ans);  
       }  
    }  
    return 0;  
    }  
大数阶乘位数:使用 log10(n)分解
//floor返回不大于x的最大整数
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

typedef long long ll;
const int maxn=1e7+7;

int main(){
    int T;
    ll n;
    double ans;
    scanf("%d",&T);
    while(T--){
        scanf("%I64d",&n);
        ans=1;
        for(int i=1; i<=n; i++){
            ans+=log10(i);
        }
        printf("%I64d
",(ll)floor(ans));
    }
}
View Code



原文地址:https://www.cnblogs.com/Tianwell/p/11442585.html