数论常见问题

数论

一、质数

1、定义:大于1的整数中如果只包含1和本身这两个约数就被称为质数或素数

2、试除法

bool is_prime(int n)
{
    if(n < 2)   return false;
    //不推荐i <= sqrt(n)这样每次都会算一遍sqrt(),也不推荐i*i <= n这样可能会爆int
    for(int i = 2; i <= n / i; i ++)
    if(n % i == 0)  
        return false;
    return true;
}

时间复杂度:sqrt(n);

3、分解质因数

试除法

思路:从小到大枚举n的所有约数

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

时间复杂度:logn~sqrt(n)当n = 2^k时除k次就会把2除尽此时除了logn次

筛质数

埃氏筛质数

思路:判断p是否为质数并不需要判断2~p - 1是否都能被p整除,只需要判断其中的质数即可


inline void get_prime(int n)
{
    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;
    }
}

时间复杂度:n(ln n + c),(其中c为欧拉常数) <nlogn

质数定理 : 1~n中有n/lnn个质数

线性筛质数

n只会被n的最小质因子筛掉

inline void get_prime(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;
        }
    }
}

约数

试除法

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

时间复杂度:sqrt(n)

约数个数

约数个数是把一个数分解质因数之后所有质因数的指数都加1的乘积之和

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

typedef long long ll;
const int mod = 1e9 + 7;
unordered_map<int, int>primes;
int n;
int main()
{
    cin >> n;
    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 ans = 1;
    for(auto x : primes)    ans = ans * (x.second + 1) % mod;
    cout << ans << endl;
    return 0;
}

欧拉函数

fai(N):1~N中所有与N互质的数的个数

#include <iostream>
using namespace std;

int main()
{
    int n;cin >> n;
    while(n --)
    {
        int x;  cin >> x;
        int ans = x;
        for(int i = 2; i <= x / i; ++i)
        {
            if(x % i == 0)  ans = ans / i * (i - 1);
            while(x % i == 0)   x /= i;
        }
        if(x > 1)   ans = ans / x * (x - 1);
        cout << ans << endl;
    }
    return 0;
}

线性筛求欧拉函数

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int primes[N], cnt, phi[N], n;
bool st[N];
ll get_eruler(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[i * primes[j]] = phi[i] * primes[j]; 
                break;
            }
            phi[i * primes[j]] = phi[i] * (primes[j] - 1); 
        }
    }
    ll res = 0;
    for(int i = 1; i<= n; ++ i) res += phi[i];
    return res;
}
int main()
{
    cin >> n;
    cout << get_eruler(n);
    return 0;
}

快速幂

在o(logk)的时间内求出来a^k mod p的结果

#include <iostream>

using namespace std;
typedef long long ll;

int a, k, p;

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 --)
    {
        scanf("%d%d%d", &a, &k, &p);
        printf("%d\n", qmi(a, k, p));
    }
    return 0;
}

扩展欧几里得

#include <iostream>

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

奇怪的表达式

#include<iostream>
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;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

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

高斯消元


组合数

思路:组合数的一个公司递推
公式的推导从i个物品中选j个,可以分为两大类第i个物品选或不选,选的话只需要冲i-1个物品中选j - 1个物品即可,不选的话,从i-1个物品选j个物品

#include <iostream>
using namespace std;

const int N = 2010, mod = 1e9 + 7;
int c[N][N], n;

int main()
{
    cin >> n;
    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;
        }
    while(n --)
    {
        int a, b;cin >> a >> b;
        cout << c[a][b] << endl;
    }
    return 0;
}

逆元法求组合数

#include<iostream>
using namespace std;

typedef long long ll;
const int N = 1e5 + 10, mod = 1e9 + 7;
int f[N], inf[N];

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

int main()
{
    f[0] = inf[0] = 1;
    for(int i = 1; i < N; ++ i)
    {
        f[i] = (ll) f[i - 1] * i % mod;
        inf[i] = (ll) inf[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
    int n; cin >> n;
    while(n --)
    {
        int a, b; scanf("%d%d", &a, &b);
        ll ans = (ll) f[a] * inf[b] % mod * inf[a - b] % mod;
        cout << ans << endl;
    }
    return 0;
}

卢卡斯定理求组合数

#include<iostream>
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)
{
    if(b > a) return 0;
    ll 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;
}

ll lucas(ll a, ll b, int p)
{
    if(a < p && b < p)  return C(a, b);
    return (ll) lucas(a % p, b % p, p) * lucas(a / p, b / p, p) % p; 
}
int main()
{
    int n;cin >> n;
    while(n --)
    {
        ll a, b; cin >> a >> b >> p;
        cout << lucas(a, b, p) << endl;
    }
    return 0;
}

卡特兰数

#include<iostream>
using namespace std;
const int mod = 1e9 + 7;
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;
        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;
}
posted @ 2022-10-27 21:20  cxy8  阅读(34)  评论(0编辑  收藏  举报