数学知识.

质数

  • 0和1既不是质数也不是合数

acwing866试除法判定质数

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

using namespace std;


bool isPrime(int x){
    if(x < 2) return false;
    for (int i = 2; i <= x / i; i ++ ){
        if(x % i == 0) return false;
    }
    return true;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int x;
        scanf("%d", &x);
        if(isPrime(x)) printf("Yes\n");
        else printf("No\n");
    }
}

acwing867分解质因数

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

using namespace std;
typedef pair<int, int> PII;

vector<PII> divide(int x){
    vector<PII> v;
    for (int i = 2; i <= x / i; i ++ ){
        if(x % i == 0){
            int c = 0;
            while (x % i == 0){
                c ++;
                x /= i;
            }
            v.push_back({i, c});
        }
    }
    if(x > 1) v.push_back({x, 1});
    return v;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int x;
        scanf("%d", &x);
        auto v = divide(x);
        for(PII p: v) printf("%d %d\n", p.first, p.second);
        printf("\n");
    }
}

acwing868筛质数

埃氏筛选法(简单易懂效率还行)

  • 埃氏筛选法:若i是质数,则n倍i不是质数
/**
 * 埃氏筛选法:若i是质数,则n倍i不是质数
 **/
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;

bool st[N];
int n;

int primes(){
    int res = 0;
    for (int i = 2; i <= n; i ++ ){
        if(!st[i]){
            //若i是质数
            res ++;
            //i的倍数都不是质数
            for (int j = 2; j * i <= n; j ++ ) st[i * j] = 1;
        }
    }
    return res;
}

int main(){
    scanf("%d", &n);
    printf("%d", primes());
}

线性筛法

#include <iostream>

using namespace std;

const int N = 1e6 + 10;
int primes[N];
int cnt;
bool st[N];
int n;

void line(int n){
    for (int i = 2; i <= n; i ++ ){
        //没被筛选过,是质数
        if(!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j ++ ){
            //把primes[j]倍i的数筛去,跟埃氏筛选法差不多
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}

int main()
{
    cnt = 0;
    scanf("%d", &n);
    line(n);
    //prime[0~cnt-1]都是质数
    printf("%d", cnt);
}

约数

acwing869试除法求约数

  • 核心:若i能整除n,则n/i也能整除n
/**
 * 核心:若i能整除n,则n/i也能整除n
 **/
 
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

vector<int> divide(int x){
    vector<int> v1, v2;
    for (int i = 1; i <= x / i; i ++ ){
        if(x % i == 0){
            v1.push_back(i);
            if(x / i != i) v2.push_back(x / i);
        }
    }
    for (int i = v2.size() - 1; i >= 0; i -- ) v1.push_back(v2[i]);
    return v1;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n --  > 0){
        int x;
        scanf("%d", &x);
        auto v = divide(x);
        for (int i = 0; i < v.size(); i ++ ) printf("%d ", v[i]);
        printf("\n");
    }
}

acwing870约数个数

任 何 一 个 整 数 d 都 可 以 表 示 为 若 干 质 数 的 乘 积 , 即 任何一个整数d都可以表示为若干质数的乘积,即 d,
d = a 1 b 1 ∗ a 2 b 2 ∗ . . . ∗ a n b n , 其 中 a i 为 质 数 , b i 是 指 数 d = a_1^{b1}*a_2^{b2}*...*a_n^{bn},其中a_i为质数,b^i是指数 d=a1b1a2b2...anbn,ai,bi
则 约 数 个 数 之 和 为 : 则约数个数之和为:
( b 1 + 1 ) ( b 2 + 1 ) ∗ . . . ∗ ( b n + 1 ) (b1 + 1)(b2 +1)*...*(bn +1) (b1+1)(b2+1)...(bn+1)
因此本题做法是分解质因数,再套公式

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


using namespace std;
typedef long long LL;

const int mod = 1e9 + 7; 
unordered_map<int, int> m;

void divide(int x){
    for (int i = 2; i <= x / i; i ++ ){
        if(x % i == 0){
            int cnt = 0;
            while(x % i == 0){
                cnt++;
                x /= i;
            }
            m[i] += cnt;
        }
    }
    if(x > 1) m[x] += 1;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int x;
        scanf("%d", &x);
        divide(x);
    }
    LL res = 1;
    for(auto e: m) res = (LL)res * (e.second + 1) % mod;
    printf("%d", res);
}

acwing871约数之和

约 数 乘 积 之 和 约数乘积之和
( a 1 0 + a 1 1 + . . . + a 1 b 1 ) ∗ ( a 2 0 + a 2 1 + . . . + a 2 b 2 ) ∗ . . . ∗ ( a n 0 + a n 1 + . . . + a n b n ) (a_1^0+ a_1^1+...+a_1^{b1})*(a_2^0+ a_2^1+...+a_2^{b2})*...*(a_n^0+ a_n^1+...+a_n^{bn}) (a10+a11+...+a1b1)(a20+a21+...+a2b2)...(an0+an1+...+anbn)
分解质因数,再套公式

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


using namespace std;
typedef long long LL;

const int mod = 1e9 + 7; 
unordered_map<int, int> m;

void divide(int x){
    for (int i = 2; i <= x / i; i ++ ){
        if(x % i == 0){
            int cnt = 0;
            while(x % i == 0){
                cnt++;
                x /= i;
            }
            m[i] += cnt;
        }
    }
    if(x > 1) m[x] += 1;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int x;
        scanf("%d", &x);
        divide(x);
    }
    LL res = 1;
    for (auto p: m){
        int a = p.first, b = p.second;
        LL t1 = 0;
        for (int k = 0; k <= b; k ++ ){
            LL t2 = 1;
            for (int z = 1; z <= k; z ++ ){
                t2 = t2 * a % mod;
            }
            t1 = t1 % mod + t2 % mod; 
        }
        res = res * t1 % mod ;
    } 
    printf("%d", res);
}

acwing872最大公约数

欧几里得算法

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

using namespace std;

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

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%d\n", gcd(a, b));
    }
}

欧拉函数

acwing873欧拉函数

在这里插入图片描述
分解质因数+套公式

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

using namespace std;

typedef long long LL;

unordered_map<int, int> divide(int x){
    unordered_map<int, int> m;
    for (int i = 2; i <= x / i; i ++ ){
        if(x % i == 0){
            int cnt = 0;
            while(x % i == 0) cnt++, x/= i;
            m[i] = cnt;
        }
    }
    if(x > 1) m[x] += 1;
    return m;
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n --  > 0){
        int x;
        scanf("%d", &x);
        unordered_map<int, int> m = divide(x);
        
        LL t1 = x, t2 = 1;
        for(auto p: m){
            t1 *= (p.first - 1);
            t2 *= p.first;
        }
        printf("%d\n", t1 / t2);
    }
}

acwing874筛法求欧拉函数

1.若i是质数,则1~i-1均与i互质,即phi[i]=i-1
2.当 i % p r i m e s [ j ] = = 0 i\%primes[j]==0 i%primes[j]==0:primes[j]是i的最小质因子,也是primes[j]*i的最小质因子。因为 1 − 1 p r i m e s [ j ] 1-\frac{1}{primes[j]} 1primes[j]1在phi[i]计算过了,只需乘primes[j]倍就是 p h i [ p r i m e s [ j ] ∗ i ] phi[primes[j] * i] phi[primes[j]i],即 p h i [ p r i m e s [ j ] ∗ i ] phi[primes[j] * i] phi[primes[j]i]=phi[i]*primes[j]
3.当i % primes[j] != 0:primes[j]不是i的质因数,只是primes[j]*i的最小质因子,因此不仅需要乘primes[j]倍,还需要补上 1 − 1 p r i m e s [ j ] 1-\frac{1}{primes[j]} 1primes[j]1,即phi[primes[j]*i]=phi[i]*(primes[j] - 1)
在这里插入图片描述

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

using namespace std;
typedef long long LL;

const int N = 1e6 + 10;
int phi[N], primes[N], cnt;
bool st[N];
int n;

void getPrimes(int x){
    phi[1] = 1;
    for (int i = 2; i <= n; i ++ ){
        if(!st[i]){
            phi[i] = i - 1;
            //存下来
            primes[cnt++] = i;
        }
        for (int j = 0; primes[j] <= n / i; j ++ ){
            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);
        }
    }    
}

int main()
{
    cnt = 0;
    scanf("%d", &n);
    getPrimes(n);
    
    LL res = 0;
    for (int i = 1; i <= n; i ++ ) res += phi[i];
    printf("%lld", res);
}

快速幂

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

using namespace std;
typedef long long LL;

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

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

acwing876快速幂求逆元

有逆元的前提

  • 整数b,m互质
  • 满足 b ∣ a b|a ba
    定义:若满足 a / b ≡ a ∗ x ( m o d n ) a/b \equiv a * x(mod n) a/bax(modn),则称x为b的模m的乘法逆元
    注意:当m为质数时, b m − 2 b^{m-2} bm2即为b的乘法逆元

总结:求a*b同余a*x模m,若m能整除a,无解,否则就是qmi(a,m-2,m)(当m为质数)

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

using namespace std;
typedef long long LL;

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

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int a, p;
        scanf("%d%d", &a, &p);
        if(a % p == 0) printf("impossible\n");
        else printf("%d\n", qmi(a, p - 2, p));
    }
}

扩展欧几里得算法

扩展欧几里得算法k可以求出一组 x i , y i x_i,y_i xi,yi,使其满足 a i x i + b i y i = g c d ( a i , b i ) a_ix_i+b_iy_i=gcd(a_i,b_i) aixi+biyi=gcd(ai,bi)
当 b = 0 时 a x + b y = a 故 而 x = 1 , y = 0 当 b=0时 ax+by=a 故而 x=1,y=0 b=0ax+by=ax=1,y=0

acwing877

#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 main(){
    int n;
    scanf("%d", &n);
    while (n --  > 0){
        int a, b, x, y;
        scanf("%d%d", &a, &b);
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
}

acwing878线性同余方程

1. a ∗ x ≡ b ( m o d   m ) 等 价 于 a ∗ x − b 是 m 的 倍 数 , 设 为 − y 倍 , 则 有 a ∗ x + m y = b a * x\equiv b(mod \ m) 等价于a *x - b是m的倍数,设为-y倍,则有 a*x + my = b axb(mod m)axbmyax+my=b
2. 根 据 裴 蜀 定 理 , 当 且 仅 当 g c d ( a , m ) ∣ b 时 有 解 根据裴蜀定理,当且仅当gcd(a,m)|b时有解 gcd(a,m)b
3. 扩 展 欧 几 里 得 算 法 可 以 求 出 一 组 x 0 , y 0 , 即 a ∗ x 0 + m y 0 = g c d ( a , m ) = d ; 要 让 d 变 为 b , 整 个 式 子 需 要 乘 b d ; 所 以 x = x 0 ∗ b d 扩展欧几里得算法可以求出一组x_0,y_0,即a*x_0+my_0=gcd(a,m)=d;要让d变为b,整个式子需要乘\frac{b}{d};所以x=x_0*\frac{b}{d} x0,y0ax0+my0=gcd(a,m)=ddbdbx=x0db

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

using namespace std;
typedef long long LL;


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 main(){
    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int a, b, m, x, y;
        scanf("%d%d%d", &a, &b, &m);
        int d = exgcd(a, m, x, y);
        if(b % d != 0) printf("impossible\n");
        else printf("%d\n", (LL)x * b / d % m);
    }
}

组合数

acwing885

C a b = C a − 1 b − 1 + C a − 1 b C_{a}^{b}=C_{a-1}^{b-1}+C_{a-1}^{b} Cab=Ca1b1+Ca1b

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

using namespace std;

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

LL f[N][N];

int main(){
    //a
    for (int i = 0; i < N; i ++ ){
        //b
        for (int j = 0; j <= i; j ++ ){
            //c(n, 0) = 1
            if(j == 0) f[i][j] = 1;
            else f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % mod;
        }
    }
    int n;
    scanf("%d", &n);
    while (n -- ){
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%lld\n", f[a][b]);
    }
}

acwing886

C a b = a ! b ! ∗ ( a − b ) ! = a ! ∗ b ! − 1 ∗ ( a − b ) ! − 1 ( q − 1 表 示 q 的 逆 元 ) C_{a}^{b}=\frac{a!}{b!*(a-b)!}=a! *{b!}^{-1}*{(a-b)!}^{-1}(q^{-1}表示q的逆元) Cab=b!(ab)!a!=a!b!1(ab)!1(q1q)
1e9+7是质数,b的逆元为 b p − 2 b^{p-2} bp2

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

using namespace std;
typedef long long LL;

const int N = 1e5 + 10, mod = 1e9 + 7;
LL fact[N], infact[N];

int qmi(int a, int k, int p){
    a = a % p;
    int res = 1;
    while (k > 0){
        if(k & 1 == 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;
        //i的逆元
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }

    int n;
    scanf("%d", &n);
    while (n -- > 0){
        int a, b;
        scanf("%d%d", &a, &b);

        printf("%d\n", (LL)fact[a] * infact[a - b] % mod * infact[b] % mod);
    }
}

acwing887

在这里插入图片描述

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

using namespace std;
typedef long long LL;

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

LL C(LL a, LL b, LL p){
    LL 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 % p;
}

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

int main()
{
    int n;
    scanf("%d", &n);
    while (n --  > 0){
        LL a, b, p;
        scanf("%lld%lld%lld", &a, &b, &p);
        printf("%d\n", lucas(a, b, p));
    }
}

acwing889

卡特兰数: C 2 n n − C 2 n n − 1 = C 2 n n n + 1 C_{2n}^n - C_{2n}^{n-1} = \frac{C_{2n}^n}{n+1} C2nnC2nn1=n+1C2nn
跟上题一样

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

using namespace std;
const int mod = 1e9 + 7;
typedef long long LL;

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

int main()
{
    int n;
    scanf("%d", &n);
    int res = 1;
    for (int i = 1, j = n + 1; i <= n ;i ++, j++ ){
        res = (LL)res * j % mod;
        res = (LL)res * qmi(i, mod - 2, mod) % mod;
    }
    res = (LL) res * qmi(n + 1, mod - 2, mod) % mod;
    printf("%d", res);
}

容斥原理

加上减多的,减少加多的

能被整除的数

在这里插入图片描述

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

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

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) scanf("%d", &p[i]);;

    int res = 0;
    //枚举每个状态,
    for(int i = 1; i < 1 << m; i++) {
        //选中集合对应质数的乘积
        int t = 1; 
        //选中的集合数量
        int s = 0;             
        //枚举当前状态的每一位
        for(int j = 0; j < m; j++){
            //选中一个集合
            if(i >> j & 1){
                //乘积大于n, 则n/t = 0, 跳出这轮循环
                if((LL)t * p[j] > n){    
                    t = -1;
                    break;
                }
                s++;                  //有一个1,集合数量+1
                t *= p[j];
            }
        }
        if(t == -1) continue;  
        //选中奇数个集合, 则系数应该是1, n/t为当前这种状态的集合数量
        if(s & 1 == 1) res += n / t;
        //反之则为 -1
        else res -= n / t;                      
    }
    printf("%d", res);
    return 0;
}

Nim游戏

acwing891

在这里插入图片描述

#include <iostream>

using namespace std;

int main()
{
    int n;
    scanf("%d", &n);
    int r = 0;
    while (n -- ){
        int t;
        scanf("%d", &t);
        r ^= t;
    }
    if(r == 0) printf("No");
    else printf("Yes");
}

acwing892

在这里插入图片描述

#include <iostream>

using namespace std;

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

acwing893

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值