数论题目与题解

15 篇文章 0 订阅
1 篇文章 0 订阅

组合数公式C(n,m)=n*(n-1)……(n-m+1)/m!

885.求组合数 I

链接:https://www.acwing.com/problem/content/887/
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(10^9+7) 的值。

f[i][j]=f[i-1][j-1]+f[i-1][j]
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=2005;
int f[N][N],a,b,n;
int main(){
    for(int i=0;i<=2000;i++){
        for(int j=0;j<=i;j++){
            if(!j)f[i][j]=1;
            else f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
        }
    }
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a>>b;
        cout<<f[a][b]<<endl;
    }
    return 0;
}

886.求组合数 II

链接:https://www.acwing.com/problem/content/888/
给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b)mod(109+7) 的值。n<=10000,a,b<=1e5

逆元求解,C(a,b)=a!/b!/(a-b)!mod 1e9+7,

//https://www.acwing.com/problem/content/888/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
const int mod=1e9+7;
int n,a,b;
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;
        k>>=1;
        a=(LL)a*a%p;
    }
    return res;
}
int main(){
    cin>>n;
    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;
    }
    while(n--){
        cin>>a>>b;
        cout<<(LL)fact[a]*infact[a-b]%mod*infact[b]%mod<<endl;
    }
    return 0;
}

887.求组合数 III
链接:https://www.acwing.com/problem/content/889/

给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(a,b)modp 的值。
1≤n≤20,1≤b≤a≤1e18,1≤p≤1e5。
lucas定理,C(a,b)%p,p为质数,C(a,b)%p=C(a%p,b%p)*C(a/p,b/p)%p。

//https://www.acwing.com/problem/content/889/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,p;
LL a,b;
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 C(int a,int b,int p){
    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);
    else return (LL)C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}
int main(){
    cin>>n;
    while(n--){
        cin>>a>>b>>p;
        cout<<lucas(a,b,p)<<endl;
    }
    return 0;
}

888.求组合数 IV
链接:https://www.acwing.com/problem/content/890/
输入 a,b,求 C(a,b) 的值。
注意结果可能很大,需要使用高精度计算,a,b<=5000

用线性筛求出所有的质数,之后统计每个质数出现的次数即可,然后高精度统计即可

//https://www.acwing.com/problem/content/890/
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
bool flag[N];
int primes[N],cnt,sum[N],a,b;
void get_primes(int x){
    for(int i=2;i<=x;i++){
        if(flag[i]==0)primes[++cnt]=i;
        for(int j=1;primes[j]<=x/i;j++){
            flag[primes[j]*i]=1;
            if(i%primes[j]==0)break;
        }
    }
}
int get(int x,int p){
    int res=0;
    while(x){
        res+=x/p;
        x/=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=t+a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(t){
        c.push_back(t%10);
        t/=10;
    }
    return c;
}
int main(){
    cin>>a>>b;
    get_primes(a);
    for(int i=1;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=1;i<=cnt;i++){
        for(int j=1;j<=sum[i];j++){
            res=mul(res,primes[i]);
        }
    }
    for(int i=res.size()-1;i>=0;i--)cout<<res[i];
    cout<<endl;
    return 0;
}

889.满足条件的01序列
链接:https://www.acwing.com/problem/content/891/
给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。

输出的答案对 1e9+7 取模。
在这里插入图片描述
由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 (0,0)(0,0) 走到 (n−1,n+1)(n−1,n+1) 的一条路径(如灰色路径)。而任何一条 (0,0)(0,0) 走到 (n−1,n+1)(n−1,n+1) 的路径,也对应了一条从 (0,0)(0,0) 走到 (n,n)(n,n) 的不合法路径。

答案如图,即卡特兰数。

//https://www.acwing.com/problem/content/891/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int 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(){
    cin>>n;
    int a=n*2,b=n,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;
    cout<<(LL)res*qmi(n+1,mod-2,mod)%mod<<endl;
    return 0;
}

890.能被整除的数
链接:https://www.acwing.com/problem/content/892/
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。

容斥原理即可。

//https://www.acwing.com/problem/content/892/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,mul,p[20],ans;
int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++)cin>>p[i];
    for(int i=1;i<(1<<m);i++){
        int mul=1,cnt=0;
        for(int j=0;j<m;j++){
            if((i>>j)&1){
                if((LL)mul*p[j]>n){
                    mul=-1;
                    break;
                }
                mul=mul*p[j];
                cnt++;
            }
        }
        if(mul!=-1){
            if(cnt&1)ans+=n/mul;
            else ans-=n/mul;
        }
    }
    cout<<ans<<endl;
    return 0;
}

876.快速幂求逆元

给定 n 组 ai,pi,其中 pi 是质数,求 ai 模 pi 的乘法逆元,若逆元不存在则输出 impossible。

注意:请返回在 0∼p−1 之间的逆元。

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

//https://www.acwing.com/problem/content/878/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,a,p;
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(){
    cin>>n;
    while(n--){
        cin>>a>>p;
        if(a%p==0)cout<<"impossible"<<endl;
        else cout<<qmi(a,p-2,p)<<endl;
    }
    return 0;
}

当n为质数时,可以用快速幂求逆元:
a / b ≡ a * x (mod n)
两边同乘b可得 a ≡ a * b * x (mod n)
即 1 ≡ b * x (mod n)
同 b * x ≡ 1 (mod n)
由费马小定理可知,当n为质数时
b ^ (n - 1) ≡ 1 (mod n)
拆一个b出来可得 b * b ^ (n - 2) ≡ 1 (mod n)
故当n为质数时,b的乘法逆元 x = b ^ (n - 2)

当n不是质数时,可以用扩展欧几里得算法求逆元:
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1
exgcd(a, p, x, y)

高斯消元
链接:https://www.acwing.com/problem/content/885/

#include<bits/stdc++.h>
using namespace std;
const int N=105;
const double eps=1e-8;
double a[N][N];
int n;
int gauss(){
    int c,r;
    for(r=0,c=0;c<n;c++){
        int t=r;
        for(int i=r+1;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[r][i],a[t][i]);
        for(int i=c+1;i<=n;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[i][c]*a[r][j];
                }
            }
        }
        r++;
    }
    if(r<n){
        for(int i=r;i<n;i++){
            if(fabs(a[i][n])>eps)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 t=gauss();
    if(t==2)cout<<"No solution"<<endl;
    else if(t==1)cout<<"Infinite group solutions"<<endl;
    else{
        for(int i=0;i<n;i++){
            if(fabs(a[i][n])<eps)a[i][n]=0;
            printf("%.2lf\n",a[i][n]);
        }
    }
    return 0;
}

欧拉函数
链接:https://www.acwing.com/problem/content/875/
给定 n 个正整数 ai,请你求出每个数的欧拉函数。
1∼N 中与 N 互质的数的个数被称为欧拉函数

1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N=p1^a1*p2^a2…pm^am,则:
ϕ(N) = N×(p1−1)/p1×(p2−1)/p2×…×(pm−1)pm

筛法求欧拉函数
链接:https://www.acwing.com/solution/content/3952/
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。n<=1e6

在用线性筛的时候,可以顺便求出欧拉函数。

//https://www.acwing.com/problem/content/876/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000005;
int n,cnt;
bool flag[N];
int euler[N],primes[N];
void get_eulers(int n){
    euler[1]=1;
    for(int i=2;i<=n;i++){
        if(flag[i]==0){
            primes[++cnt]=i;
            euler[i]=i-1;
        }
        for(int j=1;primes[j]<=n/i;j++){
            int t=primes[j]*i;
            flag[t]=1;
            if(i%primes[j]==0){
                euler[t]=euler[i]*primes[j];
                break;
            }
            euler[t]=euler[i]*(primes[j]-1);
        }
    }
}
int main(){
    cin>>n;
    get_eulers(n);
    LL ans=0;
    for(int i=1;i<=n;i++)ans+=euler[i];
    cout<<ans<<endl;
}

扩展欧几里得算法

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

#include<bits/stdc++.h>
using namespace std;
int a,b,x,y,n;
int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    int g=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
int main(){
    cin>>n;
    while(n--){
        cin>>a>>b;
        exgcd(a,b,x,y);
        cout<<x<<" "<<y<<endl;
    }
    return 0;
}

线性同余方程

链接:https://www.acwing.com/problem/content/880/

//https://www.acwing.com/problem/content/880/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,a,b,m,x,y;
int exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1;y=0;
        return a;
    }
    int g=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
int main(){
    cin>>n;
    while(n--){
        cin>>a>>b>>m;
        int g=exgcd(a,m,x,y);
        if(b%g)cout<<"impossible"<<endl;
        else cout<<(LL)b/g*x%m<<endl;
    }
    return 0;
}

表达整数的奇怪方式

参考:https://www.acwing.com/problem/content/description/206/
中国剩余定理模板
在这里插入图片描述

//https://www.acwing.com/problem/content/description/206/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int ans,n;
LL a1,m1,a2,m2,k1,k2;
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    LL g=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
int main(){
    cin>>n;
    cin>>a1>>m1;
    for(int i=2;i<=n;i++){
        cin>>a2>>m2;
        LL g=exgcd(a1,a2,k1,k2);
        if((m2-m1)%g){
            ans=-1;
            break;
        }
        k1=k1*(m2-m1)/g;
        LL t=a2/g;
        k1=(k1%t+t)%t;
        m1=a1*k1+m1;
        a1=a1/g*a2;
    }
    if(ans==-1)cout<<-1<<endl;
    else cout<<(m1%a1+a1)%a1<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值