Math模板

Math

质数

【试除法判定质数】

  1. 质数和合数是针对所有大于1的 “自然数” 来定义的

  2. 所有小于等于1的整数既不是质数也不是合数.

bool is_prime(int x){
    if(x<2)return false;
    for(int i=2;i<n/i;i++){
        if(n%i==0)return false;
    }
    return true;
}
//  n/i防止越界,相对于i*i<sqrt(n);

 

【分解质因数】

算术基本定理

void divide(int x){
    for(int i=2;i<=x/i;i++){
        if(x%i==0){
            int s=0;
            while(x%i==0)x/=i,s++;
            cout<<i<<' '<<s<<endl;
        }
    }
    if(x>1)cout<<x<<' '<<1<endl;
}
//先输出p再输出指数

【筛质数】

调和级数:当n趋近于正无穷的时候,1/2+1/3+1/4+1/5+…+1/n=lnn+c.(c是欧阳常数,约等于0.577左右.).

1.朴素筛

void get_primes(int n){
    for(int i=2;i<=n;i++){
        if(st[i])continue;
        primes[cnt++]=i;
        for(int j=i+i;j<=n;j+=i){
            st[j]=true;
        }
    }
}

 

2.线性筛

void get_primes(int n){
    for(int i=2;i<=n;i++){
        if(!st[i])primes[cnt++]=i;
        for(int j=0;primes[j]<=n/i;j++){
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
 

 

约数

【试除法求约数】

vector<int>v;
void solve(int x){
    v.clear();
    for(int i=1;i<=x/i;i++){
        if(x%i==0){
            v.push_back(i);
            if(i!=x/i)  v.push_back(x/i);
        }
    }
    sort(v.begin(),v.end());
    for(int i=0;i<v.size();i++)cout<<v[i]<<' ';
    cout<<endl;
}

 

【约数个数】

给n个x,求乘积的约数个数或之和

//自己验算一下上式子正确性
void solve(int x){
    unordered_map<int,int> prime;
    for(int i=2;i<=x/i;i++){
        while(x%i==0){
            x/=i;
            prime[i]++;
        }
    }
    if(x>1)prime[x]++;
    LL ans=1;
    for(unordered_map<int,int> ::iterator i=prime.begin();i!=prime.end();i++){
        ans=ans*((*i).second+1)%mod;
    }
}

 

【约数之和】

void solve(int x){
    map<int,int> prime;
    for(int i=2;i<=x/i;i++){
          while(x%i==0){
              x/=i;
              prime[i]++;
          }
    }
    if(x>1)prime[x]++;
    long long ans=1;
    for(map<int,int>::iterator i=prime.begin();i!=prime.end();i++){
        long long a=(*i).first,b=(*i).second;
        long long t=1;
        while(b--)t=(t*a+1)%mod;
        ans=ans*t%mod;
    }
    cout<<ans<<endl;
}

 

 

【欧几里得算法】

gcd(a,b) = gcd(b,a mod b)

int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}

欧拉函数

【欧拉函数】

互质是指最大公约数是1

//容斥原理证明欧拉函数
//1到n互质的个数即为(总个数-每个约数的倍数的个数,这些肯定不互质,一些会多,所以再加回来)
//奇减偶加
int phi(int x){
    int res = x;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0){
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);
    return res;
}

 

【筛法求欧拉函数】

LL solve(){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!st[i])prime[cnt++]=i,phi[i]=i-1;
        for(int j=0;prime[j]<=n/i;j++){
            st[prime[j]*i]=true;
            if(i%prime[j]==0){
                phi[prime[j]*i]=phi[i]*prime[j];
                break;
            }
            phi[prime[j]*i]=phi[i]*(prime[j]-1);
        }
    }
}

 

欧拉定理:如果a与n互质,则有a的phi[n]次方模n等于1

当n为质数时(a为任意自然数),phi[n]=n-1;则a的n-1次方模n等于1(费马定理)

快速幂

【快速幂】

//快速求出a的k次方mod P 的结果
//预处理a的2的1次方到2的logk次方的结果
//把k拆成若干个2的次幂之和
LL qmi(int a,int k,int p){
    LL res=1;
    while(k){
        if(k&1){             //看 k的个位,是1的话,让res*a的2的几次幂
            res=(LL)res*a%p;
        }
        k>>=1;               //接着走
        a=(LL)a*a%p;         //a方
    }
    return res%p;
}

 

【快速幂求逆元】

三大余数定理:

加法:(a+b)%c=a%c+b%c

减法:(a-b)%c=a%c-b%c

乘法:(ab)%c=(a%c)(b%c)

乘法逆元的定义

a存在模p的乘法逆元的充要条件是gcd(a,p) = 1

若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(mod m),则称 x 为 b 的模 m 乘法逆元,记为 b的-1次方(mod m)。 b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,b的(m−2)次方 即为 b 的乘法逆元。

//求其a的p-2次方即为其乘法逆元
if(a%p==0)puts("impossible");
else cout<<qmi(a,p-2,p)<<endl;

 

扩展欧几里得算法

裴蜀定理: 对任何[整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程(称为裴蜀等式):

ax + by = m 有解当且仅当m是d的倍数,d其实就是最小可以写成ax+by的正整数

【扩展欧几里得算法】

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

 

【线性同余方程】

ai×xi≡bi(modmi)

中国剩余定理

 

 

高斯消元

【高斯消元解线性方程组】

int gauss(){
    int r,c;
    for(c=0,r=0;c<n;c++){                        //枚举每一列
        int t=r;
        for(int i=r;i<n;i++){                    //从r行开始(上面的每一行第一个数都已成为一)
            if(fabs(a[i][c])>fabs(a[t][c]))
                t=i;                             //找到c这一列最大的数所对应的行,为了后续便又把它交换你上去
        }
        
        if(fabs(a[t][c])<eps)continue;           //是0的话就继续
        
        for(int i=c;i<=n;i++)swap(a[t][i],a[r][i]);    //将此行交换上去
        for(int i=n;i>=c;i--)a[r][i]/=a[r][c];         //将这行第一个数变为1
        
        for(int i=r+1;i<n;i++){                 //将从r到n行对应的此列都变为0
            if(fabs(a[i][c])>eps){
                for(int j=n;j>=c;j--){
                    a[i][j]-=a[r][j]*a[i][c];   //a[i][c]倍
                }
            }
        }
        r++;
    }
    
    if(r<n){
        for(int i=r;i<n;i++){
            if(fabs(a[i][n])>eps)
            return 2;//无解      一个数=0
        }
        return 1;                           //无穷解0=0,此方程可以被别的方程表示
    }
    
    for(int i=n-1;i>=0;i--)                 //求解
        for(int j=i+1;j<n;j++)
            a[i][n]-=a[j][n]*a[i][j];
    
    return 0;
}

 

组合数学

一、直接算

void init(){
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j <= i; j ++ )
            if (!j) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
//公式,dp

 

二、公式+逆元

fact[0]=1,infact[0]=1;
for(int i=1;i<=N;i++){
    fact[i]=(LL)fact[i-1]*i%mod;
    infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
}
cout<<fact[a]*infact[b]%mod*infact[a-b]%mod<<endl;

 

三、Lucas定理

 

int C(int a,int b,int c){
    if(b>a)return 0;
    int res=1;
    for(int i=1,j=a;i<=b;i++,j--){
        res=(LL)res*j%p;
        res=(LL)res*qmi(i,p-2,p)%p;
    }
    return res;
}
int lucas(LL a,LL b,int p){
    if(a<p&&b<p)return C(a,b,p);
    return (LL)C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}

四、高精度算出结果

int primes[N], cnt;
int sum[N];
bool st[N];
void get_primes(int n){
    for (int i = 2; i <= n; i ++ ){
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ ){
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}
int get(int n, int p){
    int res = 0;
    while (n){
        res += n / p;
        n /= p;
    }
    return res;
}
vector<int> mul(vector<int> a, int b){
    vector<int> c;
    int t = 0;
    for (int i = 0; i < a.size(); i ++ ){
        t += a[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }
    while (t){
        c.push_back(t % 10);
        t /= 10;
    }
    return c;
}
int main(){
    int a, b;
    cin >> a >> b;
    get_primes(a);
    for (int i = 0; i < cnt; i ++ ){
        int p = primes[i];
        sum[i] = get(a, p) - get(a - b, p) - get(b, p);
    }
    vector<int> res;
    res.push_back(1);
    for (int i = 0; i < cnt; i ++ )
        for (int j = 0; j < sum[i]; j ++ )
            res = mul(res, primes[i]);
    for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
    puts("");
    return 0;
}

 

五、Catalan数

/*给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序  列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。*/
//画图,0往右走,1往上走,所有到达n,n点的合法(即0的个数比1多方案数就是总的减去终点经过上一条线对称的点的方案数
 

容斥原理

二项式定理

容斥原理

for(int i=0;i<m;i++)cin>>p[i];
    for(int i=1;i<1<<m;i++){     //2的m次方,1表示选此数
        int cnt=0,t=1;           //cnt统计1个数,奇加偶减
        for(int j=0;j<m;j++){    //t统计选的这些数相乘
            if(i>>j&1){
                if((LL)t*p[j]>n){
                    t=-1;
                    break;
                }
                t*=p[j];
                cnt++;
            }
        }
        if(t!=-1){
            if(cnt%2){
                res+=n/t;
            }
            else res-=n/t;
        }
    }
    cout<<res<<endl;

 

博弈论

Nim游戏

给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。

n堆石子亦或为1时,先手必胜,否则必败

while(n--){
        int x;
        cin>>x;
        res^=x;
    }
    if(res)puts("Yes");
    else puts("No");
 
 
posted @ 2022-10-19 23:32  Dengpc  阅读(43)  评论(0)    收藏  举报