Acwing 第四章 数学知识

基础课第四章 数学知识:质数、约数、欧拉函数、快速幂、exgcd、高斯消元、求组合数、容斥原理

一、质数

866. 试除法判定质数

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

using namespace std;
const int N = 105;

bool isprime(int x)
{
    if(x<2) return false;
    for(int i=2;i<=x/i;i++) //用i<=x/i多快好省,用sqrt(x)很慢,用i*i<=x可能爆int
    {
        if(x%i==0) return false;
    }
    return true;
}

int main()
{
    int n,x;
    cin>>n;
    while(n--)
    {
        cin>>x;
        if(isprime(x)) puts("Yes");
        else puts("No");
    }
    return 0;
}

867. 分解质因数

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

using namespace std;

void depart(int x)
{
    for(int i=2;i<=x/i;i++) //n中最多只含有一个大于sqrt(n)的因子
    //对其进行优化,先考虑比sqrt(n)小的
    {
        if(x%i==0) //i一定是质数
        {
            int s=0;
            while(x%i==0)
            {
                x = x/i;
                s++;
            }
            cout<<i<<" "<<s<<endl;
        }
    }
    if(x > 1) cout<<x<<" "<<1<<endl; //如果n还是>1,说明这就是大于sqrt(n)的唯一质因子
    puts("");
}

int main()
{
    int n,x;
    cin>>n;
    while(n--)
    {
        cin>>x;
        depart(x);
    }
    return 0;
}

线形筛素数法(埃筛法) O(N)

868. 筛质数

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

using namespace std;

const int N = 1e6+10;
int n;
int prime[N],cnt; //素数数组及其个数
bool st[N]; //是否被筛除

void get_primes(int n) //埃式筛法
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])
        {
            prime[cnt++] = i;
            for(int j=i*2;j<=n;j+=i) //只将所有质数的倍数筛去
            {
                st[j] = true;
            }
        }
    }
}
void get_primes(int n) //线性法
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) prime[cnt++] = i;
        for(int j=0;prime[j] <= n/i;j++)
        {
            st[prime[j]*i] = true;
            if(i % prime[j] == 0) break; //prime[j]一定是i的最小质因子
        }
    }
}
int main()
{
    cin>>n;
    get_primes(n);
    cout<<cnt<<endl;
    return 0;
}

二、约数

869. 试除法求约数

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
const int M = 105;
int n,x;
vector<int>divisior(int x)
{
    vector<int>res;
    for(int i=1;i<=x/i;i++)
    {
        if(x%i==0)
        {
            res.push_back(i);
            if(x/i != i) res.push_back(x/i); //当遇到i为x的根号时,只能输出一次约数
        }
    }
    sort(res.begin(),res.end());//题目要求结果有序
    return res;
}

int main()
{
    cin>>n;
    while(n--)
    {
        cin>>x;
        vector<int> v = divisior(x);
        for(auto t:v)
        {
            cout<<t<<" ";
        }
        puts("");
    }
    return 0;
}

870. 约数个数

image
image
思路:对于每个因数,指数从0到α-1共α个供选择构成约数,故得到公式

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
const int M = 2e9+10,MOD = 1e9+7;
unordered_map<int,int>primes; //用哈希表来存储所有质因数的指数
int n,x;
typedef long long LL; //答案非常大,用LL存储
void get_cnt(int x)
{
    for(int i=2;i<=x/i;i++) //求x的质因数及其指数
    {
        while(x%i==0)
        {
            primes[i]++;
            x = x/i;
        }
    }
    if(x>1) primes[x]++; //大于sqrt(x)的质因数
}
int main()
{
    cin>>n;
    while(n--)
    {
        cin>>x;
        get_cnt(x);
    }
    LL res = 1;
    for(auto k:primes)
    {
        res = (res * (k.second+1)) % MOD ; //注意:每次乘完都需要取余
    }
    cout<<res<<endl;
    return 0;
}

871. 约数之和

image

image

#include<iostream>
#include<map>
#include<algorithm>

using namespace std;
typedef long long LL;
const int N = 2e9+10,MOD = 1e9+7;
unordered_map<int,int>primes;//用哈希表存储质因数及其指数
int n,x;

void get_primes(int x)//求质因数及其指数
{
    for(int i=2;i<=x/i;i++)
    {
        while(x%i==0)
        {
            primes[i]++;
            x = x/i;
        }
    }
    if(x>1) primes[x]++;//处理大于sqrt(x)的质因数
}
int main()
{
    cin>>n;
    while(n--)
    {
        cin>>x;
        get_primes(x);
    }
    LL res = 1;
    for(auto prime:primes)
    {
        LL tmp = 1;
        int p = prime.first;//质因数
        int e = prime.second;//指数
        while(e--)//注意循环次数为e,不是e-1
        {
            tmp = (tmp * p + 1) % MOD; // p^0 + p^1 +……+p^e秦九韶算法,注意要取模
        }
        res = (res * tmp) % MOD;//注意每次运算对答案也取模
    }
    cout<<res<<endl;
    return 0;
}

872. 最大公约数

#include<iostream>
using namespace std;
typedef long long LL;
int n;
int a,b;
int gcd(int a,int b)
{
   return b==0?a:gcd(b,a%b); 
}
int main()
{
    cin>>n;
    while(n--)
    {
        cin>>a>>b;
        cout<<gcd(a,b)<<endl;
    }
    return 0;
}

三、欧拉函数

定义

image

873. 欧拉函数

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

using namespace std;

int n,x;

int main()
{
    cin>>n;
    while(n--)
    {
        cin>>x;
        int res = x;
        for(int i=2;i<=x/i;i++)
        {
            if(x%i==0)//i是x的一个质因数
            {
                res = (res / i) * (i-1); //先除法后乘法,防止溢出
                while(x%i==0)//除干净
                {
                    x = x/i;
                }
            }
        }
        if(x>1) res = (res/x) *(x-1);//先除法后乘法,防止溢出
        cout<<res<<endl;
    }
    return 0;
}

874. 筛法求欧拉函数

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
int primes[N],cnt;
int phi[N];
bool st[N];

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++)
        {
            st[primes[j]*i] = true;
            if(i % primes[j] == 0)
            {
                phi[primes[j] * i] = phi[i] * primes[j];
                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;
}

四、快速幂

875. 快速幂

image

image

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

typedef long long LL;

const int N = 1e5 + 10;

LL a,k,p;

LL qmi(LL a,LL k,LL p) //a^k % p log(k)的时间内算出
{
    LL 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;
    cin>>n;
    while (n -- )
    {
        scanf("%lld%lld%lld", &a, &k,&p);
        cout<<qmi(a,k,p)<<endl;
    }
    return 0;
}

876. 快速幂求逆元

image

a/b mod p 相当于 a*(power(a,p-2) mod p)

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

using namespace std;
typedef long long LL;

const int N = 1e5 + 10;

LL qmi(LL a,LL b,LL p)
{
    LL res = 1;
    while(b)
    {
        if(b&1) res = (LL)(res*a)%p;
        b >>= 1;
        a = (LL)a*a%p;
    }
    return res;
}

int n;
LL a,p;
int main()
{
   scanf("%d",&n);
   while(n--)
   {
       scanf("%lld%lld", &a, &p);
       LL t = qmi(a,p-2,p);
       if(a%p) cout<<t<<endl;
       else puts("impossible");
   }
    return 0;
}

五、扩展欧几里得

877. 扩展欧几里得算法

image

image
image

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

using namespace std;

const int N = 1e5 + 10;

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x = 1,y = 0;
        return a;
    }
    int d = exgcd(b,a%b,y,x);
    y -= a/b * x;
    return d;
}
int n,a,b,x,y;
int main()
{
    scanf("%d", &n);
    while (n -- )
    {
        scanf("%d%d", &a, &b);
        exgcd(a,b,x,y);
        cout<<x<<' '<<y<<endl;
    }
    return 0;
}

六、高斯消元

883. 高斯消元解线性方程组

image
//: # (打卡模板,上面预览按钮可以展示预览效果 ^^)

#include<iostream>
#include<cmath>

using namespace std;
const int N = 105;
const double eps = 1e-6;
double a[N][N+1];
int n;

int gauss()
{
    int c,r;//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;//若该列全为0,说明已处理完毕,前进到下一列
        
        for(int i = c;i < n+1;i++) swap(a[t][i],a[r][i]);//把最大行与当前一轮处理的第一行交换(不是矩阵第一行)
        for(int i = n;i >= c;i--) a[r][i] = a[r][i] / a[r][c];//把最大行乘以某个数,使得第一个数变成1
        for(int i = r + 1;i < n;i++)//将下面所有行的第c列消成0,i正序枚举行
        {
            if(fabs(a[i][c]) > eps)//大于0
            {
                for(int j = n;j >= c;j--)//j倒序枚举列
                {
                    a[i][j] = a[i][j] - a[r][j] * a[i][c];//a[i][c]第i行第c个数,此处难理解
                }
            }
        }
        r++;
    }
    if(r < n)
    {
        for(int i = r;i < n;i++)
        {
            if(fabs(a[i][n]) > eps) return 0;//无解
        }
        return 2;//无穷多解
    }
    for(int i = n-1;i >= 0;i--)//将矩阵化简到:系数矩阵有且只有一个1,即可直接求出未知数的值
    {
        for(int j = i + 1;j <= n;j++)
        {
            a[i][n] = a[i][n] - a[i][j] * a[j][n];
        }
    }
    return 1;//唯一解
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n+1;j++)
        {
            cin>>a[i][j];
        }
    }
    int res = gauss();
    if(res == 0) puts("No solution");
    else if(res == 1)
    {
        for(int i = 0;i < n;i++)
        {
            printf("%.2lf\n",a[i][n]);
        }
    }
    else if(res == 2) puts("Infinite group solutions");
    return 0;
}

七、求组合数

C(a,b) = C(a-1,b) + C(a-1,b-1)

image

#include<iostream>
using namespace std;
const int N = 2005,MOD = 1e9+7;
int c[N][N];//c[a][b] 表示Cba b在上,a在下

void init()//预处理,打表
{
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(!j) c[i][j] = 1;//定义规定 i个中选0个的方案数为1
            else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % MOD; //必须取余,否则溢出
        }
    }
}

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

快速幂+逆元

image
//: # (打卡模板,上面预览按钮可以展示预览效果 ^^)

#include<iostream>
using namespace std;
const int N = 1e5+10,MOD = 1e9+7;
typedef long long LL;
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 = k/2;
        a = (LL)a * a % p;
    }
    return res;
}

int main()
{
    int n,a,b;
    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;//快速幂求逆元
    }
    cin>>n;
    while(n--)
    {
        cin>>a>>b;
        cout<<(LL)fact[a] * infact[b] % MOD * infact[a-b] % MOD<<endl;//cab组合数公式
    }
    return 0;
}

卢卡斯定理

image

#include<iostream>

using namespace std;

typedef long long LL;

LL a,b;
int p,n;

int qmi(int a,int b)
{
    int res = 1;
    while(b)
    {
        if(b & 1) res = (LL)res * a % p; //遇到乘法后面就要跟取余
        b = b / 2;
        a = (LL)a * a % p;
    }
    return res;
}

int C(LL a,LL 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()
{
    cin>>n;
    while(n--)
    {
        cin>>a>>b>>p;
        cout<<Lucas(a,b)<<endl;
    }
    return 0;
}

高精度

image

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 5005;
int primes[N],sum[N],cnt;
bool st[N];
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 = t / 10;
    }
    while(t)
    {
        c.push_back(t % 10);
        t = t / 10;
    }
    return c;
}

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)//对p的各个<=a的次数算整除 下取整倍数
{
    int res = 0;
    while(n)
    {
        res += n/p;
        n = n/p;
    }
    return res;
}
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--)
    {
        cout<<res[i];
    }
    puts("");
    return 0;
}

八、容斥原理

890. 能被整除的数

image

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

const int N = 20;
int p[N],n,m;

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 mul = 1;//选中的集合对应质数的乘积,用于计算集合中数的数量
        int cnt = 0;//选中集合的个数
        for(int j = 0;j < m;j++)//把i看作二进制数,枚举i的每一位
        {
            if(i >> j & 1)//若该位为1,则表明选中该集合
            {
                if((LL)mul * p[j] > n) // 乘积大于n,则n/mul = 0,不选中此集合,跳出此轮循环
                {
                    mul = -1;//标记乘法异常,不选中
                    break;
                }
                cnt++; // 有一个1,集合的个数加1
                mul = mul * p[j];
            }
        }
        if(mul == -1) continue;// 乘积大于n,则n/mul = 0,不选中此集合,跳出此轮循环
        
        if(cnt & 1 == 1) res += n / mul;//根据容斥原理,集合个数为奇数时加,偶数时减
        else res -= n / mul;
    }
    cout<<res<<endl;
    return 0;
}
posted @   安河桥北i  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示