ACM essential template - Number Theory

Keywords: Algorithm number theory ICPC

number theory

Prime table

Number of prime numbers with length i
cnt [ 1 ] = 4 
cnt [ 2 ] = 21
cnt [ 3 ] = 143
cnt [ 4 ] = 1061 
cnt [ 5 ] = 8363 
cnt [ 6 ] = 68906
cnt [ 7 ] = 586081

Prime table within 1000

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
53 59 61 67 71 73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173 179 181 191 193 197
199 211 223 227 229 233 239 241 251 257 263 269 271 277 281
283 293 307 311 313 317 331 337 347 349 353 359 367 373 379
383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
467 479 487 491 499 503 509 521 523 541 547 557 563 569 571
577 587 593 599 601 607 613 617 619 631 641 643 647 653 659
661 673 677 683 691 701 709 719 727 733 739 743 751 757 761
769 773 787 797 809 811 821 823 827 829 839 853 857 859 863
877 881 883 887 907 911 919 929 937 941 947 953 967 971 977
983 991 997
  • There are 664579 primes (less than 7e5) within 81e7

Determination of prime number by trial division

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

Decomposition of prime factor by trial division

void divide(int x)
{
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            int s = 0;
            while (x % i == 0) x /= i, s ++ ;
            cout << i << ' ' << s << endl;
        }
    if (x > 1) cout << x << ' ' << 1 << endl;
    cout << endl;
}

Simple sieve method for finding prime numbers

int primes[N], cnt;     // Primes [] stores all primes
bool st[N];         // st[x] is storage x screened out

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (st[i]) continue;
        primes[cnt ++ ] = i;
        for (int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}

Finding prime number by linear sieve method

int primes[N], cnt;     // Primes [] stores all primes
bool st[N];         // st[x] is storage x screened out

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

 

Try division to find all divisors

vector<int> get_divisors(int x)
{
    vector<int> res;
    for (int i = 1; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res.push_back(i);
            if (i != x / i) res.push_back(x / i);
        }
    sort(res.begin(), res.end());
    return res;
}

Sum of divisors and divisors

If N = p1^c1 * p2^c2 * ... *pk^ck
 Approximate number: (c1 + 1) * (c2 + 1) * ... * (ck + 1)
Sum of divisors: (p1^0 + p1^1 + ... + p1^c1) * ... * (pk^0 + pk^1 + ... + pk^ck)

Euclidean algorithm - maximum common divisor

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

Finding Euler function

int phi(int x)
{
    int res = x;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);

    return res;
}

Finding Euler function by sieve method

int primes[N], cnt;     // Primes [] stores all primes
int euler[N];           // Euler function that stores each number
bool st[N];         // st[x] is storage x screened out


void get_eulers(int n)
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            int t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0)
            {
                euler[t] = euler[i] * primes[j];
                break;
            }
            euler[t] = euler[i] * (primes[j] - 1);
        }
    }
}

 

Counting

seek m^k mod p,Time complexity O(logk). 

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

extended euclidean algorithm

// Find x, y so that ax + by = gcd(a, b)
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;
}

Gauss elimination

// a[N][N] is an augmented matrix
int gauss()
{
    int c, r;
    for (c = 0, r = 0; c < n; c ++ )
    {
        int t = r;
        for (int i = r; i < n; i ++ )   // The row with the largest absolute value was found
            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[t][i], a[r][i]);      // Change the row with the largest absolute value to the top
        for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];      // Changes the first row of the current row to 1
        for (int i = r + 1; i < n; i ++ )       // Cancel all the following columns with the current row to 0
            if (fabs(a[i][c]) > eps)
                for (int j = n; j >= c; j -- )
                    a[i][j] -= a[r][j] * a[i][c];

        r ++ ;
    }

    if (r < n)
    {
        for (int i = r; i < n; i ++ )
            if (fabs(a[i][n]) > eps)
                return 2; // unsolvable
        return 1; // There are infinite sets of solutions
    }

    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; // There is a unique solution
}

 

Finding combinatorial number by recursive method

// c[a][b] represents the number of schemes to select b from a apple
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;

The combination number is obtained by preprocessing the inverse element

Firstly, the remainder of all factorial modulus is preprocessed fact[N],And the inverse of all factorial modules infact[N]
If the modular number is a prime number, the inverse element can be solved by Fermat's small theorem
int qmi(int a, int k, int p)    // Fast power template
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

// Remainder of preprocessing factorial and remainder of factorial inverse
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;
}

 

Lucas theorem

if p Is a prime number, then for any integer 1 <= m <= n,yes:
    C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)

int qmi(int a, int k, int p)  // Fast power template
{
    int res = 1 % p;
    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)  // Finding combinatorial number C(a, b) by theorem
{
    if (a < b) return 0;

    LL x = 1, y = 1;  // x is the numerator and y is the denominator
    for (int i = a, j = 1; j <= b; i --, j ++ )
    {
        x = (LL)x * i % p;
        y = (LL) y * j % p;
    }

    return x * (LL)qmi(y, p - 2, p) % p;
}

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

 

Solving combinatorial number by decomposing prime factor method

When we need to find the true value of the combined number rather than the remainder of a number, the method of decomposing the prime factor is easier to use:
    1. Sieve method to find all prime numbers in the range
    2. adopt C(a, b) = a! / b! / (a - b)! This formula calculates the number of times of each quality factor. n! in p The number of times n / p + n / p^2 + n / p^3 + ...
    3. Multiply all prime factors with high-precision multiplication

int primes[N], cnt;     // Store all prime numbers
int sum[N];     // The number of times each prime number is stored
bool st[N];     // Has each number stored been screened out


void get_primes(int n)      // Finding prime number by linear sieve method
{
    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)       // Please n! Number of times in
{
    int res = 0;
    while (n)
    {
        res += n / p;
        n /= p;
    }
    return res;
}


vector<int> mul(vector<int> a, int b)       // High precision by low precision template
{
    vector<int> c;
    int t = 0;
    for (int i = 0; i < a.size(); i ++ )
    {
        t += a[i] * b;
        c.push_back(t % 10);
        t /= 10;
    }

    while (t)
    {
        c.push_back(t % 10);
        t /= 10;
    }

    return c;
}

get_primes(a);  // All prime numbers in the preprocessing range

for (int i = 0; i < cnt; i ++ )     // Find the number of times of each prime factor
{
    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 ++ )     // Multiply all prime factors with high-precision multiplication
    for (int j = 0; j < sum[i]; j ++ )
        res = mul(res, primes[i]);

Author: yxc
 Link: https://www.acwing.com/blog/content/406/
Source: AcWing
 The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

Catalan number

Given n zeros and N ones, they are arranged in a sequence with a length of 2n in some order. The number of sequences satisfying that the number of zeros in any prefix is not less than 1 is: Cat(n) = C(2n, n) / (n + 1)

NIM games

Given N stacks of items, there are Ai items in stack i. Two players take turns. They can choose one pile at a time and take away any number of items. They can take away one pile, but they can't take it away. Whoever takes the last item wins. Both took the best strategy and asked whether the first hand would win.

We call this game NIM game. The state faced in the process of the game is called situation. The first action in the whole game is called the first hand, and the second action is called the second hand. If in a situation, no matter what action is taken, the game will be lost, it is said that the situation will be lost.
The so-called taking the optimal strategy means that if there is an action in a certain situation, which makes the opposite face face a doomed situation after the action, the action will be taken first. At the same time, such a situation is called victory. The game problems we discuss generally only consider the ideal situation, that is, the result of the game when both of them have no mistakes and take the optimal strategy action.
There is no draw in NIM game. There are only two situations: the first hand must win and the first hand must lose.

Theorem: NIM game must win if and only if A1 ^ A2 ^... ^ an= 0

Fair combination game ICG

If a game meets:

Two players act alternately;
At any time in the game process, the legal actions that can be performed have nothing to do with which player's turn;
Players who cannot act will be judged negative;

The game is called a fair combination game.
NIM game belongs to fair combination game, but urban construction chess games, such as go, are not fair combination games. Because the two belligerents of go can only drop sunspots and whites respectively, the judgment of victory and defeat is also complex, which does not meet conditions 2 and 3

Directed graph game

Given a directed acyclic graph, the graph has a unique starting point on which a chess piece is placed. Two players alternately move the piece along the directed edge. They can move one step at a time. If they can't move, they will be judged negative. The game is called directed graph game.
Any fair combination game can be transformed into a directed graph game. The specific method is to regard each situation as a node in the graph, and there are directional edges from each bureau to the next situation that can be reached along the legal action.

Mex operation

Let S represent a set of nonnegative integers. mex(S) is defined as the operation to find the minimum nonnegative integer that does not belong to the set S, that is:
mex(S) = min{x}, x is a natural number, and X does not belong to S

SG function

In the directed graph game, for each node x, let there be k directed edges starting from X and reaching nodes y1, y2,..., yk respectively, define SG(x) as the set composed of the SG function values of the successor nodes y1, y2,..., yk of X, and then execute the result of mex(S) operation, that is:
SG(x) = mex({SG(y1), SG(y2), ..., SG(yk)})
In particular, the SG function value of the whole directed graph game G is defined as the SG function value of the starting point s of the directed graph game, that is, SG(G) = SG(s).

Sum of directed graph games

Let G1, G2,..., Gm be m digraph games. A directed graph game G is defined. Its action rule is to select any directed graph game Gi and take one step on Gi. G is called the sum of directed graph games G1, G2,..., Gm.
The SG function value of the sum of the digraph game is equal to the XOR sum of the SG function values of each sub game it contains, that is:
SG(G) = SG(G1) ^ SG(G2) ^ ... ^ SG(Gm)

theorem

A situation in a digraph game is certain to win if and only if the SG function value of the corresponding node is greater than 0.
A situation in a digraph game must fail if and only if the SG function value of the corresponding node is equal to 0.

High precision four arithmetic

addition
  • Non pressure level
#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }

    if (t) C.push_back(t);
    return C;
}

int main()
{
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    auto C = add(A, B);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl;

    return 0;
}

  • Press 9
#include <iostream>
#include <vector>

using namespace std;

const int base = 1000000000;

vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % base);
        t /= base;
    }

    if (t) C.push_back(t);
    return C;
}

int main()
{
    string a, b;
    vector<int> A, B;
    cin >> a >> b;

    for (int i = a.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- )
    {
        s += (a[i] - '0') * t;
        j ++, t *= 10;
        if (j == 9 || i == 0)
        {
            A.push_back(s);
            s = j = 0;
            t = 1;
        }
    }
    for (int i = b.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- )
    {
        s += (b[i] - '0') * t;
        j ++, t *= 10;
        if (j == 9 || i == 0)
        {
            B.push_back(s);
            s = j = 0;
            t = 1;
        }
    }

    auto C = add(A, B);

    cout << C.back();
    for (int i = C.size() - 2; i >= 0; i -- ) printf("%09d", C[i]);
    cout << endl;

    return 0;
}

 
subtraction
#include <iostream>
#include <vector>

using namespace std;

bool cmp(vector<int> &A, vector<int> &B)
{
    if (A.size() != B.size()) return A.size() > B.size();

    for (int i = A.size() - 1; i >= 0; i -- )
        if (A[i] != B[i])
            return A[i] > B[i];

    return true;
}

vector<int> sub(vector<int> &A, vector<int> &B)
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ )
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    vector<int> C;

    if (cmp(A, B)) C = sub(A, B);
    else C = sub(B, A), cout << '-';

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl;

    return 0;
}

 
multiplication
#include <iostream>
#include <vector>

using namespace std;


vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}


int main()
{
    string a;
    int b;

    cin >> a >> b;

    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    auto C = mul(A, b);

    for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);

    return 0;
}

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

using namespace std;

vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- )
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a;
    vector<int> A;

    int B;
    cin >> a >> B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    int r;
    auto C = div(A, B, r);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];

    cout << endl << r << endl;

    return 0;
}

 

;
}

###### division

```cpp
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- )
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a;
    vector<int> A;

    int B;
    cin >> a >> B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    int r;
    auto C = div(A, B, r);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];

    cout << endl << r << endl;

    return 0;
}

 

Posted by deeem on Wed, 01 Dec 2021 13:14:09 -0800