数学算法板子库


这里的python全是python3的
详情请见acwing,acwing赛高
力求快、准、狠、短


#质数

##试除法判断质数

----c++版

#include<iostream>
#include<algorithm>
using namespace std;

bool is_prime(int n){
    if(n<2)return false;
    for(int i=2;i<=n/i;i++)//这样写n不会溢出
        if(n%i==0)return false;
    return true; 
}

int main(){
    int n;scanf("%d",&n);
    while(n--){
        int t;scanf("%d",&t);
        if(is_prime(t))puts("Yes");
        else puts("No");
    }
    return 0;
}

##试除法分解质因数

----c++版

https://www.acwing.com/problem/content/869/

//n中最多只包含一个大于根号n的质因子
#include<iostream>
#include<algorithm>
using namespace std;

void divide(int n){
    for(int i=2;i<=n/i;i++)//把拼成一个数的所有质数从小到大排列,并一个个取出来并计数
        if(n%i==0){//i一定是质数
            int s=0;
            while(n%i==0){
                n/=i;
                s++;
            }
            printf("%d %d\n",i, s);
        }
    if(n>1)printf("%d %d\n",n,1); // 处理唯一一个大于根号n的质因子
}

int main(){
    int n;scanf("%d",&n);
    while(n--){
        int t;scanf("%d",&t);
        divide(t);
        puts("");
    }
    return 0;
}

##质数筛

----c++版埃氏筛

o(nloglogn)

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int primes[N],cnt;
int st[N];
void get_primes(int n){
    if(n<2)return;
    for(int i=2;i<=n;i++){
        if(!st[i]){
            primes[cnt++]=i;
            for(int j=i+i;j<=n;j+=i)st[j]=true;
        }
    }
}
int main(){
    int n;cin>>n;
    cnt=0;
    get_primes(n);
    printf("%d",cnt);
    return 0;
}

----c++版线性筛

o(n)

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int primes[N],cnt;
int st[N];
//线性筛,相当于杀掉所有以2为最小质因子的数、以3为最小质因子的数---以x为最小质因子的数
//但整个过程是分开进行的,妙啊,妙啊
//n只会被其最小质因子筛掉
void get_primes(int n){
    if(n<2)return;
    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 main(){
    int n;cin>>n;
    cnt=0;
    get_primes(n);
    printf("%d",cnt);
    return 0;
}

#约数

##试除法求约数

----c++版

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

vector<int> get_dividers(int n){
    vector<int>res;
    for(int i=1;i<=n/i;i++)
        if(n%i==0){
            res.push_back(i);
            if(i!=n/i)res.push_back(n/i);
        }
        sort(res.begin(), res.end());
        return res; 
}

int main(){
    int n;cin>>n;
    while(n--){
        int t;cin>>t;
        auto res=get_dividers(t);
        for(auto k:res)cout<<k<<' ';
        cout<<endl;
    }
    return 0;
}

##约数个数

基于算数基本定理
有公式的,自行百度

----c++版

#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
//int范围内的约数最多的数的约数有1650个
int main(){
    int n;cin>>n;
    unordered_map<int,int>primes;
    while(n--){
        int x;cin>>x;
        for(int i=2;i<=x/i;i++)
            while(x%i==0){
                x/=i;
                primes[i]++;
            }
        if(x>1)primes[x]++;
    }
    
    ll res=1;
    for(auto prime:primes)res=res*(prime.second+1)%mod;
    cout<<res;
    return 0;
}

##约数之和

----c++版

#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
typedef long long ll;

int main(){
    int n;cin>>n;
    unordered_map<int,int>primes;
    while(n--){
        int x;cin>>x;
        for(int i=2;i<=x/i;i++)
            while(x%i==0){
                x/=i;
                primes[i]++;
            }
        if(x>1)primes[x]++;
    }
    
    ll res=1;
    for(auto prime:primes){
        int p=prime.first,a=prime.second;
        ll t=1;
        while(a--)t=(t*p+1)%mod;
        res=res*t%mod;
    }
    cout<<res;
    return 0;
}

##最大公约数(欧几里得算法/辗转相除法)

----c++版

#include<iostream>
using namespace std;

int gcd(int a,int b){ //两个gcd约数集合是一样的
    return b?gcd(b,a%b):a;//a和0的最大公约数是a
}

int main(){
    int n;cin>>n;
    while(n--){
        int a,b;cin>>a>>b;
        cout<<gcd(a,b)<<endl;
    }
    return 0;
}

#欧拉函数

https://www.acwing.com/problem/content/875/

----c++版

定义法

//Φ(n)表示1-n中与n互质的数的个数, 互质就是gcd=1
//用容斥原理证明公式
#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    int n;cin>>n;
    while(n--){
        int a;cin>>a;
        int res=a;
        for(int i=2;i<=a/i;i++)
            if(a%i==0){
                res=res/i*(i-1);//整数,先除再乘不会越界
                while(a%i==0)a/=i;
            }
        if(a>1)res=res/a*(a-1);
        cout<<res<<endl;
    }
    
    return 0;
}

##筛法求欧拉函数

----c++版

https://www.acwing.com/problem/content/876/

//求1-n中每一个数的欧拉函数
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e7+10;

int primes[N],cnt;
int phi[N];
bool st[N];

typedef long long ll; //和最大是n方,会爆int 

ll get_eulers(int n){//接用线性筛的模板
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!st[i]){
            primes[cnt++]=i;
            phi[i]=i-1;
        }for(int j=0;primes[j]<=n/i;j++){///注意n/i
            st[primes[j]*i]=true;
            if(i%primes[j]==0){
                phi[primes[j]*i]=primes[j]*phi[i];
                break;
            }
            phi[primes[j]*i]=phi[i]*(primes[j]-1);
        }
    }
    ll res=0;
    for(int i=1;i<=n;i++)res+=phi[i];
    return res;
}

int main(){
    int n;cin>>n;
    cout<<get_eulers(n)<<endl;
    return 0;
}

欧拉定理:若a与n互质,则a的phi(n)次方与n同余1
在这里插入图片描述

#快速幂

----c++版

https://www.acwing.com/problem/content/877/

#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;

int qmi(int a,int k,int p){
    int res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        k>>=1;
        a=(ll)a*a%p;//预处理下一个
    }
    return res;
}

int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        int a,k,p;
        scanf("%d%d%d",&a,&k,&p);
        printf("%d\n",qmi(a,k,p));
    }
    return 0;
}

##快速幂求逆元

----c++版

https://www.acwing.com/problem/content/878/

//本质上还是求快速幂
//联系费马定理
//有可能无解
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;

int qmi(int a,int k,int p){
    int res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        k>>=1;
        a=(ll)a*a%p;//预处理下一个
    }
    return res;
}

int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        int a,p;
        scanf("%d%d",&a,&p);
        
        int res=qmi(a,p-2,p);
        if(a%p)printf("%d\n",res);
        else puts("impossible");/无解
    }
    return 0;
}

#扩展欧几里得算法

裴蜀定理:对任意正整数a、b,那么存在整数x、y使得ax+by等于a和b的最大公约数

----c++版

https://www.acwing.com/problem/content/879/

#include<iostream>
#include<algorithm>
using namespace std;

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;
}

int main(){
    int n;scanf("%d",&n);
    while(n--){
        int a,b,x,y;
        scanf("%d%d",&a,&b);
        exgcd(a,b,x,y);
        printf("%d %d\n",x,y);
    }
    return 0;
}

##线性同余方程

----c++版

https://www.acwing.com/problem/content/880/
在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

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;
}

int main(){
    int n;scanf("%d",&n);
    while(n--){
        int a,b,m,x,y;
        scanf("%d%d%d",&a,&b,&m);
        int d=exgcd(a,m,x,y);
        if(b%d)puts("impossible");
        else printf("%lld\n",(ll)x*(b/d)%m);
    }
    return 0;
}

#中国剩余定理

在这里插入图片描述
一些基础知识
在这里插入图片描述
在这里插入图片描述

----c++版

https://www.acwing.com/problem/content/206/
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long ll;

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

int main(){
    int n;cin>>n;
    bool has_ans=true;
    ll a1,m1;
    cin>>a1>>m1;
    for(int i=0;i<n-1;i++){
        ll a2,m2;
        cin>>a2>>m2;
        ll k1,k2;
        ll d=exgcd(a1, a2, k1, k2);
        if((m2-m1)%d){
            has_ans=false;
            break;
        }
        k1*=(m2-m1)/d;
        ll t=a2/d;
        k1=(k1%t+t)%t;
        
        m1=a1*k1+m1;
        a1=abs(a1/d*a2);
    }
    if(has_ans){
        cout<<(m1%a1+a1)%a1<<endl;
    }else{
        puts("-1");
    }
    return 0;
}

#高斯消元法

https://www.acwing.com/problem/content/885/

----c++版

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=110;
const double eps=1e-6;

int n;
double a[N][N];

int gauss(){
    int c,r;
    for(c=0,r=0;c<n;c++){
        int t=r;
        for(int i=r;i<n;i++)
            if(fabs(a[i][c])>fabs(a[t][c]))
                t=i;
        if(fabs(a[t][c])<eps)continue;
        for(int i=c;i<=n;i++)swap(a[t][i],a[r][i]);//有n+1列
        for(int i=n;i>=c;i--)a[r][i]/=a[r][c];
        for(int i=r+1;i<n;i++)
            if(fabs(a[i][c])>eps)
                for(int j=n;j>=c;j--)
                    a[i][j]-=a[r][j]*a[i][c];//第一个数变成0
        r++;
    }
    if(r<n){
        for(int i=r;i<n;i++)
            if(fabs(a[i][n])>eps)
                return 2;//no solution
        return 1;//infinite solutions
    }
    
    for(int i=n-1;i>=0;i--)
        for(int j=i+1;j<n;j++)
            a[i][n]-=a[i][j]*a[j][n];
    
    return 0;//only one
}

int main(){
    cin>>n;
    for (int i=0;i<n;i++)
        for(int j=0;j<n+1;j++)
            cin>>a[i][j];
            
    int t=gauss();
    if(t==0){
        for(int i=0;i<n;i++)printf("%.2lf\n",a[i][n]);
    }else if(t==1)puts("Infinite group solutions");
    else puts("No solution");
    
    return 0;
}

##解异或线性方程组

https://www.acwing.com/problem/content/886/

----c++版

#include<iostream>
#include<algorithm>

using namespace std;
const int N=110;

int n;
int a[N][N];

int gauss(){
    int r,c;
    for(r=c=0;c<n;c++){
        int t=r;
        for(int i=r;i<n;i++)
            if(a[i][c]){
                t=i;
                break;
            }
        if(!a[t][c])continue;
        for(int i=c;i<=n;i++)swap(a[t][i],a[r][i]);
        for(int i=r+1;i<n;i++)
            if(a[i][c])
                for(int j=c;j<=n;j++)
                    a[i][j]^=a[r][j];
        r++;
    }
    if(r<n){
        for(int i=r;i<n;i++)
            if(a[i][n])
                return 2;
        return 1;
    }
    
    for(int i=n-1;i>=0;i--)
        for(int j=i+1;j<n;j++)
            a[i][n]^=a[i][j]&a[j][n];
    return 0;
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<=n;j++)
            cin>>a[i][j];
            
    int res=gauss();
    
    if(res==0){
        for(int i=0;i<n;i++)cout<<a[i][n]<<endl;
    }else if(res==1)puts("Multiple sets of solutions");
    else puts("No solution");
    
    return 0;
}

#求组合数

##1 递推形式的

----c++版

https://www.acwing.com/problem/content/887/

#include<iostream>
#include<algorithm>

using namespace std;

const int N=2010,mod=1e9+7;

int c[N][N];
//用预处理的方式   
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;
}

int main(){
    init();
    int n;scanf("%d",&n);
    while(n--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n", c[a][b]);
    }
    return 0;
}

##2 Cab范围较大的,用预处理出阶乘的方式

----c++版

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N=100010,mod=1e9+7;

int fact[N],infact[N];

int qmi(int a,int k,int p){
    int res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        a=(ll)a*a%p;
        k>>=1;
    }
    return res;
}

int main(){
    fact[0]=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;
    }
    
    int n;scanf("%d",&n);
    while(n--){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%lld\n",(ll)fact[a]*infact[b]%mod*infact[a-b]%mod);
    }
    return 0;
}

##3 询问少,Cab比较大,卢卡斯定理

在这里插入图片描述
大约O(
在这里插入图片描述
证明:https://www.cnblogs.com/TY02/p/11813194.html
证明2:
在这里插入图片描述

----c++版

https://www.acwing.com/problem/content/889/

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int p;

int qmi(int a,int k){
    int res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        a=(ll)a*a%p;
        k>>=1;
    }
    return res;
}

int C(int a,int b){
    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;
    }
    return res;
}

int lucas(ll a,ll b){
    if(a<p&&b<p)return C(a,b);
    return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
}

int main(){
    int n;cin>>n;
    while(n--){
        ll a,b;
        cin>>a>>b>>p;
        cout<<lucas(a,b)<<endl;
    }
    return 0;
}

##4 Cab数大得批爆,需要高精度,先分解质因数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

----c++版

https://www.acwing.com/problem/content/890/

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

const int N=5010;

int primes[N],cnt;
bool st[N];
int sum[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(b,p)-get(a-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]);
    
    return 0;
}

##卡特兰数

第18位爆int
477638700

h(0)=h(1)=1
h(n)= h(0)h(n-1)+h(1)h(n-2) + … + h(n-1)h(0) (n>=2)
h(n)=h(n-1)(4*n-2)/(n+1)
h(n)=C(2n,n)/(n+1) (n=0,1,2,…)
h(n)=c(2n,n)-c(2n,n-1) (n=0,1,2,…)

在这里插入图片描述
从0,0到6,6有几种方案?
0是向右,1是向上,向右的步数必须时刻大于等于向上的步数
https://www.acwing.com/problem/content/891/

在这里插入图片描述
C a t l a n ( n ) = C 2 n n ( n + 1 ) Catlan(n) = \frac{C_{2n}^n}{(n+1)} Catlan(n)=(n+1)C2nn

----c++版

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;

int qmi(int a,int k,int p){
    int res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        a=(ll)a*a%p;
        k>>=1;
    }
    return res;
}

int main(){
    int n;cin>>n;
    
    int a=2*n,b=n;
    int res=1;
    
    for(int i=a;i>a-b;i--)res=(ll)res*i%mod;
    for(int i=1;i<=b;i++)res=(ll)res*qmi(i,mod-2,mod)%mod;
    
    res=(ll)res*qmi(n+1,mod-2,mod)%mod;
    cout<<res<<endl;
    
    return 0;
}

#容斥原理

在这里插入图片描述
在这里插入图片描述

##能被整除的数

集合求并
https://www.acwing.com/problem/content/892/

----c++版

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=20;

int n,m;
int p[N];

int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++)cin>>p[i];
    
    int res=0;
    for(int i=1;i<1<<m;i++){
        int t=1,cnt=0;
        for(int j=0;j<m;j++)
            if(i>>j&1){
                cnt++;
                if((ll)t*p[j]>n){
                    t=-1;
                    break;
                }
                t*=p[j];
            }
        if(t!=-1){
            if(cnt%2)res+=n/t;
            else res-=n/t;
        }
    }
    cout<<res<<endl;
    
    return 0;
}

#博弈论

##NIM游戏

公平组合游戏 ICG满足:

  1. 两名玩家交替行动
  2. 合法行动与玩家无关
  3. 不能行动的玩家判负

一个公平组合游戏一定可转化为有向图游戏

先手必胜状态:可以走到先手必败态
先手必败状态:只能走到先手必胜态
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
https://www.acwing.com/problem/content/893/

----c++版

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    int n,res=0;
    scanf("%d",&n);
    while(n--){
        int x;scanf("%d",&x);
        res^=x;
    }
    if(res)puts("Yes");
    else puts("No");
    return 0;
}

##台阶Nim游戏

https://www.acwing.com/problem/content/894/

----c++版

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    int n,res=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        if(i%2)res^=x;
    }
    if(res)puts("Yes");
    else puts("No");
    return 0;
}

##SG函数

在这里插入图片描述

##集合nim游戏

https://www.acwing.com/problem/content/description/895/

----c++版

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_set>

using namespace std;

const int N=110,M=10010;

int n,m;
int s[N],f[M];

int sg(int x){//记忆化搜索保证每个状态只算一次
    if(f[x]!=-1)return f[x];
    unordered_set<int>S;
    for(int i=0;i<m;i++){
        int sum=s[i];
        if(x>=sum)S.insert(sg(x-sum));
    }
    for(int i=0;;i++)
        if(!S.count(i))
            return f[x]=i;
}

int main(){
    cin>>m;
    for(int i=0;i<m;i++)cin>>s[i];
    cin>>n;
    memset(f, -1, sizeof f);
    int res=0;
    for(int i=0;i<n;i++){
        int x;
        cin>>x;
        res ^= sg(x);
    }
    
    if(res)puts("Yes");
    else puts("No");
    
    return 0;
}

##拆分nim游戏

https://www.acwing.com/problem/content/896/

----c++版

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;

const int N=110;

int f[N];

int sg(int x){
    if(f[x]!=-1)return f[x];
    unordered_set<int>S;
    for(int i=0;i<x;i++)
        for(int j=0;j<=i;j++)
            S.insert(sg(i)^sg(j));
            
    for(int i=0;;i++)
        if(!S.count(i))
            return f[x]=i;
}

int main(){
    int n;cin>>n;
    int res=0;
    
    memset(f, -1, sizeof f);
    
    for(int i=0;i<n;i++){
        int x;cin>>x;
        res^=sg(x);
    }
    
    if(res)puts("Yes");
    else puts("No");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值