数学入门(快速幂&乘法逆元&GCD&质数&组合数)

A - 快速幂

题目链接:
P1226 【模板】快速幂 - 洛谷
思路:

快速幂使用二进制拆分和乘法倍增的思想,将指数 n 写成二进制,将 a n a^n an 转化为多个 a 2 i a^{2^i} a2i 的乘积。

比如: 3 13 = 3 ( 1001 ) 2 = 3 2 3 + 3 2 2 + 3 2 0 = 3 8 + 3 4 + 3 1 3^{13}=3^{(1001)_2}=3^{2^3}+3^{2^2}+3^{2^0}=3^8+3^4+3^1 313=3(1001)2=323+322+320=38+34+31。

n 有 l o g 2 n + 1 log_2n+1 log2n+1 个二进制位,所以只需要计算出所有的 a 2 i a^{2^i} a2i ,并根据 n 的二进制形式即可求得 a n a^n an 。时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n)。
代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
int qmi(int a,int b,int p)
{
    int res=1;
    while(b){
        //如果b的二进制表示的第0位为1,则乘上当前的a
        if(b&1)res=res*a%p;
        b>>=1;
        //更新a,a依次为a^{2^0},a^{2^1},a^{2^2},....,a^{2^logb}
        a=a*a%p;
    }
    return res;
}
void solve()
{
    int a,b,p;
    cin>>a>>b>>p;
    cout<<a<<"^"<<b<<" mod "<<p<<"="<<qmi(a,b,p);
}
signed main()
{
    int t;
    t=1;
    while(t--)
    {
        solve();
    }
}

B - 矩阵快速幂

题目链接:
P3390 【模板】矩阵快速幂 - 洛谷
思路:
矩阵乘法 + 快速幂 = 矩阵快速幂 矩阵乘法+快速幂=矩阵快速幂 矩阵乘法+快速幂=矩阵快速幂。

矩阵乘法公式: c [ i ] [ j ] = ( ∑ m = 1 n ( a [ i ] [ m ] ∗ b [ m ] [ j ] ) ) c[i][j]=(\sum^n_{m=1}(a[i][m]*b[m][j])) c[i][j]=(∑m=1n(a[i][m]∗b[m][j]))
代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=110;
int a[N][N];
int ans[N][N];
int t[N][N];
const int MOD=1e9+7;
int n,k;
void multi(int a[][N],int b[][N]){
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int m=1;m<=n;m++)
            {
                t[i][j]=(t[i][j]+(a[i][m]*b[m][j])%MOD)%MOD;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            a[i][j]=t[i][j];//把t数组中计算的答案赋值回答案数组
            t[i][j]=0;//把t数组清零
        }
    }
}
void qmi()
{
    while(k){
        if(k&1) multi(ans,a);
        k>>=1;
        multi(a,a);
    }
}
void solve()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>a[i][j];
            a[i][j]%=MOD;
            ans[i][j]=0;
        }
        ans[i][i]=1;//初始化单位矩阵
    }
    qmi();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cout<<ans[i][j]<<" ";
        }
        cout<<endl;
    }
}
signed main()
{
    int t;
    t=1;
    while(t--)
    {
        solve();
    }
}

C - 模意义下的乘法逆元

题目链接:
P3811 【模板】模意义下的乘法逆元 - 洛谷
思路:

我选择记忆。

乘法逆元一般用来计算 a / b m o d    p a/b\mod\ p a/bmod p,可以先计算出 b 在模 p 意义下的逆元然后再计算。

最后的公式为 x = a p − 2 m o d    p x=a^{p-2}\mod p x=ap−2modp。a、p 要互质。

但是这个方法 TLE 了。

换线性递推公式,令 i 的逆元为 inv[i],1 的逆元为 1,我们设 p = k ∗ i + r p=k*i+r p=k∗i+r,k= ⌊ p i ⌋ \lfloor\frac{p}{i}\rfloor ⌊ip⌋, r = p % i r=p\%i r=p%i。

在 mod p 意义下,上式变为 k ∗ i + r = 0 m o d p k*i+r=0 \ mod\ p k∗i+r=0 mod p,左右同时乘 inv[i], inv[r] 得到 k ∗ i n v [ r ] + i n v [ i ] = 0 m o d p k∗inv[r]+inv[i]=0\ mod \ p k∗inv[r]+inv[i]=0 mod p。

所以 i n v [ i ] = − k ∗ i n v [ r ] m o d p inv[i]=-k*inv[r] \ mod \ p inv[i]=−k∗inv[r] mod p 即 i n v [ i ] = − ⌊ p i ⌋ ∗ i n v [ p % i ] m o d p inv[i]=-\lfloor\frac{p}{i}\rfloor*inv[p\%i] \ mod \ p inv[i]=−⌊ip⌋∗inv[p%i] mod p 。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=3e6+5;
int inv[N];
void solve()
{
    int n,p;
    cin>>n>>p;
    inv[1]=1;
    for(int i=2;i<=n;i++)
    {
        inv[i]=((-p/i)*inv[p%i]%p+p)%p;
    }
    for(int i=1;i<=n;i++)cout<<inv[i]<<endl;
}
signed main()
{
    int t=1;
    IOS
    while(t--)
    {
        solve();
    }
}

D - 有理数取余

题目链接:
P2613 【模板】有理数取余 - 洛谷
思路:

这个题目其实也是逆元+快速幂,用费马小定理的结论然后推,不需要用高精度,直接在过程中取模。

题目中的输出Angry 其实并没有作用,肯定有解。
代码:

cpp 复制代码
const int MOD=19260817;
int qmi(int a,int b,int p)
{
    int res=1;
    while(b){
        //如果b的二进制表示的第0位为1,则乘上当前的a
        if(b&1)res=res*a%p;
        b>>=1;
        //更新a,a依次为a^{2^0},a^{2^1},a^{2^2},....,a^{2^logb}
        a=a*a%p;
    }
    return res;
}
void solve()
{
    string s;
    cin>>s;
    int n=s.size();
    int a=0;
    for(int i=0;i<n;i++)
    {
        a=(a*10+(s[i]-'0'))%MOD;
    }
    cin>>s;
    n=s.size();
    int b=0;
    for(int i=0;i<n;i++)
    {
        b=(b*10+(s[i]-'0'))%MOD;
    }
    int ans=1;
    if(b==0)cout<<"Angry!";
    else{
        ans=a*qmi(b,MOD-2,MOD)%MOD;
        cout<<ans;
    }
}

E - 最大公约数和最小公倍数问题

题目链接:
P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题 - 洛谷
思路:

首先 两数乘积 = 最大公约数 ∗ 最小公倍数 两数乘积=最大公约数*最小公倍数 两数乘积=最大公约数∗最小公倍数,所以 P ∗ Q P*Q P∗Q 即为定值只需要枚举 P 来确定 Q 即可。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
void solve()
{
    int x,y;
    cin>>x>>y;
    int ans=0;
    int flag=0;
    for(int i=1;i<=x*y/i;i++)
    {
        if(x*y%i==0&&__gcd(x*y/i,i)==x){
            ans++;
            if(i*i==x*y)flag=1;
        }
    }
    cout<<ans*2-flag;
}

F - 质数筛

题目链接:
P5736 【深基7.例2】质数筛 - 洛谷
思路:

判断一个数是不是质数就是根据质数的定义来筛选,如果一个数的因数只有 1 和它本身就叫做质数。
代码:

cpp 复制代码
bool check(int x){
    if(x<2)return false;
    for(int i=2;i<=x/i;i++)
    {
        if(x%i==0)return false;
    }
    return true;
}
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        if(check(a))cout<<a<<" ";
    }
}

G - 线性筛素数

题目链接:
P3383 【模板】线性筛素数 - 洛谷
思路:

上面的太耗时了,如果一个数是已有质数的倍数那么这个数不是质数,这样我们就可以减少很多筛选。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=1e8+5;
bool st[N];
int n;
int cnt=0;
int p[N];
void get_isPrime()
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])p[cnt++]=i;
        for(int j=0;p[j]<=n/i;j++)
        {
            st[p[j]*i]=true;
            if(i%p[j]==0)break;
        }
    }
}
void solve()
{
    int k;
    cin>>k;
    cout<<p[k-1]<<endl;
}
signed main()
{
    int t;
    cin>>n>>t;
    IOS
    get_isPrime();
    while(t--)
    {
        solve();
    }
}

H - 质因子分解

题目链接:
P2043 质因子分解 - 洛谷
思路:

题目的意思是将 N ! N! N! 分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。不用把n的阶乘求出来,只需要一个循环,从2循环到n,把每个数分解的变成1即可。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=10001;
bool st[N];
int n;
int cnt=0;
int p[N];
int a[N];
void get_isPrime()
{
    for(int i=2;i<=N;i++)
    {
        if(!st[i])p[cnt++]=i;
        for(int j=0;p[j]<=N/i;j++)
        {
            st[p[j]*i]=true;
            if(i%p[j]==0)break;
        }
    }
}
void solve()
{
    cin>>n;
    for(int i=2;i<=n;i++)
    {
        int b=i;
        for(int j=0;j<cnt&&p[j]<=b;j++)
        {
            while(b%p[j]==0){
                a[p[j]]++;
                b/=p[j];
            }
        }
    }
    for(int i=2;i<=n;i++){
        if(a[i]!=0){
            cout<<i<<" "<<a[i]<<endl;
        }
    }
}
signed main()
{
    int t=1;
    IOS
    get_isPrime();
    while(t--)
    {
        solve();
    }
}

求组合数

组合数的计算: C ( n , k ) = n ! k ! ∗ ( n − k ) ! C(n,k)=\frac{n!}{k!*(n-k)!} C(n,k)=k!∗(n−k)!n!

求组合数 I

当 C a b C^b_a Cab 中 a、b 比较小时可以由公式 C a b = C a − 1 b + C a − 1 b − 1 C^b_a=C^{b}{a-1}+C^{b-1}{a-1} Cab=Ca−1b+Ca−1b−1 递推出来,用的时候直接查表即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=2005;
const int 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;
        }
    }
}
void solve()
{
    int a,b;
    cin>>a>>b;
    cout<<C[a][b]<<endl;
}
signed main()
{
    int t;
    cin>>t;
    init();
    IOS
    while(t--)
    {
        solve();
    }
}

求组合数 II

预处理出阶乘数组和阶乘的逆元数组,再进行运算。模数是质数的情况下可以用费马小定理+快速幂求逆元

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
const int N=1e5+5;
#define endl '\n'
int fact[N],infact[N];
const int MOD=1e9+7;
int qmi(int a,int b,int p)
{
    int res=1;
    while(b){
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
void init(){
    fact[0]=1,infact[0]=1;
    for(int i=1;i<=N;i++)
    {
        fact[i]=i*fact[i-1]%MOD;
        infact[i]=infact[i-1]*qmi(i,MOD-2,MOD)%MOD;
    }
}
void solve(){
    int a,b;
    cin>>a>>b;
    int ans=fact[a]*infact[a-b]%MOD*infact[b]%MOD;
    cout<<ans<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    init();
    while(t--){
        solve();
    }
}

求组合数 III

数据范围太大了,所以就不好预处理了。

Lucas 定理: C a b = C a % p b % p ∗ C a / p b / p ( m o d p ) C^b_a=C^{b\%p}{a\%p}*C^{b/p}{a/p}\ (mod \ p) Cab=Ca%pb%p∗Ca/pb/p (mod p)。就不推导了。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
const int N=1e5+5;
#define endl '\n'
int fact[N],infact[N];
int p;
int qmi(int a,int b,int p)
{
    int res=1;
    while(b){
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=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=res*j%p;
        res=res*qmi(i,p-2,p)%p;
    }
    return res;
}
int Lucas(int a,int b,int p){
    if(a<p&&b<p)return C(a,b,p);
    else{
        return C(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
    }
}
void solve(){
    int a,b;
    cin>>a>>b>>p;
    cout<<Lucas(a,b,p)<<endl;
}
signed main()
{
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
}
相关推荐
不会敲代码11 小时前
面试必考:如何优雅地将列表转换为树形结构?
javascript·算法·面试
努力学算法的蒟蒻2 小时前
day88(2.17)——leetcode面试经典150
算法·leetcode·面试
Anastasiozzzz2 小时前
LeetCode 287 寻找重复数字
算法·leetcode·职场和发展
im_AMBER2 小时前
Leetcode 123 二叉树的层平均值 | 二叉树的右视图 | 二叉树的层序遍历
数据结构·学习·算法·leetcode·二叉树
We་ct2 小时前
LeetCode 100. 相同的树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
样例过了就是过了2 小时前
LeetCode热题100 轮转数组
数据结构·算法·leetcode
ShineWinsu2 小时前
对于stack和queue经典算法题目:155. 最小栈、JZ31 栈的压入、弹出序列和102. 二叉树的层序遍历的解析
数据结构·c++·算法·面试·力扣·笔试·牛客网
能源系统预测和优化研究2 小时前
【原创改进代码】考虑电动汽车移动储能特性的多区域电网功率波动平抑优化调控
大数据·算法·能源
_F_y2 小时前
两个数组的动态规划
算法·动态规划