Basic Algorithm Templates

Contents

Graph Theory

Dynamic Programming

Basic Algorithms

1 Sorting

1.1 Quicksort Algorithm Template

—— Template Problem AcWing 785. Quicksort

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

1.2 Merge Sort Algorithm Template

—— Template Problem AcWing 787. Merge Sort

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];

    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

2.1 Integer Binary Search Algorithm Template

—— Template Problem AcWing 789. Range of Numbers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool check(int x) {/* ... */} // Check if x satisfies a certain property

// When the interval [l, r] is divided into [l, mid] and [mid + 1, r]:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check() determines if mid satisfies the property
        else l = mid + 1;
    }
    return l;
}
// When the interval [l, r] is divided into [l, mid - 1] and [mid, r]:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

2.2 Floating-Point Binary Search Algorithm Template

—— Template Problem AcWing 790. Cube Root of a Number

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
bool check(double x) {/* ... */} // Check if x satisfies a certain property

double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps represents the precision, depending on the precision requirement of the problem
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

3 High Precision

3.1 High Precision Addition

—— Template Problem AcWing 791. High Precision Addition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// C = A + B, A >= 0, B >= 0
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;
}

3.2 High Precision Subtraction

—— Template Problem AcWing 792. High Precision Subtraction

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// C = A - B, satisfying A >= B, A >= 0, B >= 0
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;
}

3.3 High Precision Multiplication by Low Precision

—— Template Problem AcWing 793. High Precision Multiplication

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// C = A * b, A >= 0, b >= 0
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;
}

3.4 High Precision Division by Low Precision

—— Template Problem AcWing 794. High Precision Division

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// A / b = C ... r, A >= 0, b > 0
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;
}

4 Prefix and Difference

Prefix: Quickly find the sum of a segment Difference: Quickly add +c to a segment, inverse operation of prefix

4.1 One-Dimensional Prefix Sum

—— Template Problem AcWing 795. Prefix Sum

1
2
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

4.2 Two-Dimensional Prefix Sum

—— Template Problem AcWing 796. Sum of Submatrices

1
2
3
S[i, j] = Sum of all elements in the upper left part of the grid at row i and column j
The sum of the submatrix with (x1, y1) as the upper left corner and (x2, y2) as the lower right corner is:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

4.3 One-Dimensional Difference

—— Template Problem AcWing 797. Difference Add c to each number in the interval [l, r]: B[l] += c, B[r + 1] -= c

4.4 Two-Dimensional Difference

—— Template Problem AcWing 798. Difference Matrix

1
2
Add c to all elements in the submatrix with (x1, y1) as the upper left corner and (x2, y2) as the lower right corner:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

5 Bit Manipulation

—— Template Problem AcWing 801. Number of 1s in Binary

1
2
Find the k-th digit of n: n >> k & 1
Return the last 1 of n: lowbit(n) = n & -n

6 Two-Pointer Algorithm

—— Template Problem AcWing 799. Longest Consecutive Non-Repeating Subsequence, AcWing 800. Target Sum of Array Elements

1
2
3
4
5
6
7
8
9
for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // Logic for specific problem
}
Common problem categories:
    (1) For a sequence, maintain an interval with two pointers
    (2) For two sequences, maintain a certain order, such as merging two ordered sequences in merge sort

7 Discretization

—— Template Problem AcWing 802. Interval Sum

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
vector<int> alls; // Store all values to be discretized
sort(alls.begin(), alls.end()); // Sort all values
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // Remove duplicate elements

// Binary search to find the discretized value corresponding to x
int find(int x) // Find the first position greater than or equal to x
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // Map to 1, 2, ...n
}

8 Interval Merging

—— Template Problem AcWing 803. Interval Merging

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Merge all intervals that have intersections
void merge(vector<PII> &segs)
{
    vector<PII> res;

    sort(segs.begin(), segs.end());

    int st = -2e9, ed = -2e9;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

    if (st != -2e9) res.push_back({st, ed});

    segs = res;
}

Data Structures

1 Linked List and Adjacency List: Storage of Trees and Graphs

1.1 Singly Linked List

—— Template Problem AcWing 826. Singly Linked List

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// head stores the head of the linked list, e[] stores the value of the node, ne[] stores the next pointer of the node, idx indicates which node is currently used
int head, e[N], ne[N], idx;

// Initialization
void init()
{
    head = -1;
    idx = 0;
}

// Insert a number a at the head of the linked list
void insert(int a)
{
    e[idx] = a, ne[idx] = head, head = idx ++ ;
}

// Delete the head node, need to ensure the head node exists
void remove()
{
    head = ne[head];
}

1.2 Doubly Linked List

—— Template Problem AcWing 827. Doubly Linked List

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// e[] represents the value of the node, l[] represents the left pointer of the node, r[] represents the right pointer of the node, idx indicates which node is currently used
int e[N], l[N], r[N], idx;

// Initialization
void init()
{
    //0 is the left endpoint, 1 is the right endpoint
    r[0] = 1, l[1] = 0;
    idx = 2;
}

// Insert a number x to the right of node a
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// Delete node a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

2 Stack and Queue: Monotonic Queue, Monotonic Stack

2.1 Stack

—— Template Problem AcWing 828. Simulate Stack

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// tt indicates the top of the stack
int stk[N], tt = 0;

// Insert a number to the top of the stack
stk[ ++ tt] = x;

// Pop a number from the top of the stack
tt -- ;

// Value at the top of the stack
stk[tt];

// Check if the stack is empty, if tt > 0, it means not empty
if (tt > 0)
{

}

2.2 Queue

—— Template Problem AcWing 829. Simulate Queue

  1. Regular Queue:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// hh indicates the head of the queue, tt indicates the tail of the queue
int q[N], hh = 0, tt = -1;

// Insert a number to the tail of the queue
q[ ++ tt] = x;

// Pop a number from the head of the queue
hh ++ ;

// Value at the head of the queue
q[hh];

// Check if the queue is empty, if hh <= tt, it means not empty
if (hh <= tt)
{
}
  1. Circular Queue
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// hh indicates the head of the queue, tt indicates the position after the tail of the queue
int q[N], hh = 0, tt = 0;

// Insert a number to the tail of the queue
q[tt ++ ] = x;
if (tt == N) tt = 0;

// Pop a number from the head of the queue
hh ++ ;
if (hh == N) hh = 0;

// Value at the head of the queue
q[hh];

// Check if the queue is empty, if hh != tt, it means not empty
if (hh != tt)
{
}

2.3 Monotonic Stack

—— Template Problem AcWing 830. Monotonic Stack Common model: Find the nearest number greater/smaller than each number on the left

1
2
3
4
5
6
int tt = 0;
for (int i = 1; i <= n; i ++ )
{
    while (tt && check(stk[tt], i)) tt -- ;
    stk[ ++ tt] = i;
}

2.4 Monotonic Queue

—— Template Problem AcWing 154. Sliding Window Common model: Find the maximum/minimum value in the sliding window

1
2
3
4
5
6
7
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // Check if the head of the queue slides out of the window
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

3 KMP

—— Template Problem AcWing 831. KMP String

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// s[] is the long text, p[] is the pattern string, n is the length of s, m is the length of p
Compute the Next array of the pattern string:
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}

// Matching
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j];
        // Logic after successful match
    }
}//C's library function strstr() is like this

4 Trie Tree

—— Template Problem AcWing 835. Trie String Statistics

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int son[N][26], cnt[N], idx;
// Node 0 is both the root node and the empty node
// son[][] stores the child nodes of each node in the tree
// cnt[] stores the number of words ending at each node

// Insert a string
void insert(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p] ++ ;
}

// Query the number of occurrences of a string
int query(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}//You can use map<string,int>, but the time is more than twice

5 Union-Find

—— Template Problem AcWing 836. Merge Sets, AcWing 837. Number of Points in Connected Components (1) Naive Union-Find:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    int p[N]; // Store the ancestor node of each point

    // Return the ancestor node of x
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // Initialization, assuming node numbers are 1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // Merge the two sets where a and b are located:
    p[find(a)] = find(b);

(2) Union-Find maintaining size:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    int p[N], size[N];
    //p[] stores the ancestor node of each point, size[] is only meaningful for ancestor nodes, indicating the number of points in the set where the ancestor node is located

    // Return the ancestor node of x
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // Initialization, assuming node numbers are 1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        size[i] = 1;
    }

    // Merge the two sets where a and b are located:
    size[find(b)] += size[find(a)];
    p[find(a)] = find(b);

(3) Union-Find maintaining distance to ancestor node:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    int p[N], d[N];
    //p[] stores the ancestor node of each point, d[x] stores the distance from x to p[x]

    // Return the ancestor node of x
    int find(int x)
    {
        if (p[x] != x)
        {
            int u = find(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    }

    // Initialization, assuming node numbers are 1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        d[i] = 0;
    }

    // Merge the two sets where a and b are located:
    p[find(a)] = find(b);
    d[find(a)] = distance; // Initialize the offset of find(a) according to the specific problem

6 Heap

—— Template Problem AcWing 838. Heap Sort, AcWing 839. Simulate Heap

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// h[N] stores the values in the heap, h[1] is the top of the heap, the left child of x is 2x, the right child is 2x + 1
// ph[k] stores the position of the k-th inserted point in the heap
// hp[k] stores the k-th point in the heap is the k-th inserted
int h[N], ph[N], hp[N], size;

// Swap two points and their mapping relationships
void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

// O(n) build heap
for (int i = n / 2; i; i -- ) down(i);

7 Hash

7.1 General Hash

—— Template Problem AcWing 840. Simulate Hash Table

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
(1) Chaining Method
    int h[N], e[N], ne[N], idx;

    // Insert a number into the hash table
    void insert(int x)
    {
        int k = (x % N + N) % N;
        e[idx] = x;
        ne[idx] = h[k];
        h[k] = idx ++ ;
    }

    // Query whether a number exists in the hash table
    bool find(int x)
    {
        int k = (x % N + N) % N;
        for (int i = h[k]; i != -1; i = ne[i])
            if (e[i] == x)
                return true;

        return false;
    }

(2) Open Addressing Method
    int h[N];

    // If x is in the hash table, return the index of x; if x is not in the hash table, return the position where x should be inserted
    int find(int x)
    {
        int t = (x % N + N) % N;
        while (h[t] != null && h[t] != x)
        {
            t ++ ;
            if (t == N) t = 0;
        }
        return t;
    }

7.2 String Hash

—— Template Problem AcWing 841. String Hash The core idea: Treat the string as a P-base number, the empirical value of P is 131 or 13331, the probability of conflict with these two values is low A small trick: Use 2^64 as the modulus, so that it can be directly stored with unsigned long long, and the result of overflow is the result of modulus

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k] stores the hash value of the first k letters of the string, p[k] stores P^k mod 2^64

// Initialization
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];
    p[i] = p[i - 1] * P;
}

// Calculate the hash value of the substring str[l ~ r]
ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

7.3 C++ STL Introduction

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
vector, Dynamic array, doubling idea
    size()  Return the number of elements
    empty()  Return whether it is empty
    clear()  Clear
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    Support comparison operations, in lexicographical order

pair<int, int>
    first, First element
    second, Second element
    Support comparison operations, with first as the first key, second as the second key (lexicographical order)

string, String
    size()/length()  Return the length of the string
    empty()
    clear()
    substr(start index, (substring length))  Return substring
    c_str()  Return the starting address of the character array where the string is located

queue, Queue
    size()
    empty()
    push()  Insert an element to the tail
    front()  Return the head element
    back()  Return the tail element
    pop()  Pop the head element

priority_queue, Priority queue, default is max heap
    size()
    empty()
    push()  Insert an element
    top()  Return the top element
    pop()  Pop the top element
    Define as min heap: priority_queue<int, vector<int>, greater<int>> q;

stack, Stack
    size()
    empty()
    push()  Insert an element to the top
    top()  Return the top element
    pop()  Pop the top element

deque, Double-ended queue
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []

set, map, multiset, multimap, Based on balanced binary tree (red-black tree), dynamically maintain ordered sequence
    size()
    empty()
    clear()
    begin()/end()
    ++, -- Return predecessor and successor, time complexity O(logn)

    set/multiset
        insert()  Insert a number
        find()  Find a number
        count()  Return the number of a certain number
        erase()
            (1) Input is a number x, delete all x   O(k + logn)
            (2) Input an iterator, delete this iterator
        lower_bound()/upper_bound()
            lower_bound(x)  Return the iterator of the smallest number greater than or equal to x
            upper_bound(x)  Return the iterator of the smallest number greater than x
    map/multimap
        insert()  Inserted number is a pair
        erase()  Input parameter is pair or iterator
        find()
        []  Note that multimap does not support this operation. Time complexity is O(logn)
        lower_bound()/upper_bound()

unordered_set, unordered_map, unordered_multiset, unordered_multimap, Hash table
    Similar to the above, the time complexity of addition, deletion, modification, and query is O(1)
    Does not support lower_bound()/upper_bound(), iterator's ++, --

bitset, Bit compression
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []

    count()  Return how many 1s

    any()  Check if there is at least one 1
    none()  Check if all are 0

    set()  Set all positions to 1
    set(k, v)  Set the k-th bit to v
    reset()  Set all bits to 0
    flip()  Equivalent to ~
    flip(k) Invert the k-th bit

Search and Graph Theory

1 DFS and BFS

1.1 Storage of Trees and Graphs

A tree is a special kind of graph, and the storage method is the same as that of a graph. For an undirected graph with edge ab, store two directed edges a->b, b->a. Therefore, we can only consider the storage of directed graphs. (1) Adjacency Matrix: g[a][b] stores edge a->b (2) Adjacency List:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// For each point k, open a singly linked list to store all points that k can reach. h[k] stores the head node of this singly linked list
int h[N], e[N], ne[N], idx;

// Add an edge a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// Initialization
idx = 0;
memset(h, -1, sizeof h);

1.2 Traversal of Trees and Graphs

Time complexity O(n+m), n represents the number of points, m represents the number of edges (1) Depth-First Traversal —— Template Problem AcWing 846. Tree’s Center

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int dfs(int u)
{
    st[u] = true; // st[u] indicates that point u has been traversed

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

(2) Breadth-First Traversal —— Template Problem AcWing 847. Level of Points in Graph

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
queue<int> q;
st[1] = true; // Indicates that point 1 has been traversed
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // Indicates that point j has been traversed
            q.push(j);
        }
    }
}

2 Traversal of Trees and Graphs Topological Sorting

—— Template Problem AcWing 848. Topological Sequence of Directed Graph Time complexity O(n+m), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool topsort()
{
    int hh = 0, tt = -1;

    // d[i] stores the in-degree of point i
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[ ++ tt] = j;
        }
    }

    // If all points are enqueued, it means there is a topological sequence; otherwise, there is no topological sequence.
    return tt == n - 1;
}

3 Shortest Path

3.1 Naive Dijkstra Algorithm

—— Template Problem AcWing 849. Dijkstra Shortest Path I Time complexity is O(n2+m), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int g[N][N];  // Store each edge
int dist[N];  // Store the shortest distance from point 1 to each point
bool st[N];   // Store whether the shortest path of each point has been determined

// Find the shortest path from point 1 to point n, if it does not exist, return -1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // Find the point with the smallest distance among the points whose shortest path has not been determined
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // Use t to update the distance of other points
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

3.2 Heap-Optimized Dijkstra

—— Template Problem AcWing 850. Dijkstra Shortest Path II Time complexity O(mlogn), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
typedef pair<int, int> PII;

int n;      // Number of points
int h[N], w[N], e[N], ne[N], idx;       // Adjacency list stores all edges
int dist[N];        // Store the distance from each point to point 1
bool st[N];     // Store whether the shortest distance of each point has been determined

// Find the shortest path from point 1 to point n, if it does not exist, return -1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first stores the distance, second stores the node number

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

3.3 Bellman-Ford Algorithm

—— Template Problem AcWing 853. Shortest Path with Edge Count Limit Time complexity O(nm), n represents the number of points, m represents the number of edges Note that in the template problem, you need to slightly modify the following template and add a backup array, see the template problem for details.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int n, m;       // n represents the number of points, m represents the number of edges
int dist[N];        // dist[x] stores the shortest path distance from 1 to x

struct Edge     // Edge, a represents the starting point, b represents the ending point, w represents the weight of the edge
{
    int a, b, w;
}edges[M];

// Find the shortest path distance from 1 to n, if it is impossible to go from 1 to n, return -1.
int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    // If the n-th iteration still relaxes the triangle inequality, it means that there is a shortest path of length n+1, and by the pigeonhole principle, there are at least two identical points in the path, indicating that there is a negative weight cycle in the graph.
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }

    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}

3.4 SPFA Algorithm (Queue-Optimized Bellman-Ford Algorithm)

—— Template Problem AcWing 851. SPFA Shortest Path Average time complexity O(m), worst case O(nm), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int n;      // Total number of points
int h[N], w[N], e[N], ne[N], idx;       // Adjacency list stores all edges
int dist[N];        // Store the shortest distance from each point to point 1
bool st[N];     // Store whether each point is in the queue

// Find the shortest path distance from point 1 to point n, if it is impossible to go from point 1 to point n, return -1
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])     // If j already exists in the queue, do not insert j repeatedly
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

3.5 SPFA to Determine if There is a Negative Cycle in the Graph

—— Template Problem AcWing 852. SPFA to Determine Negative Cycle Time complexity is O(nm), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int n;      // Total number of points
int h[N], w[N], e[N], ne[N], idx;       // Adjacency list stores all edges
int dist[N], cnt[N];        // dist[x] stores the shortest distance from point 1 to x, cnt[x] stores the number of points in the shortest path from 1 to x
bool st[N];     // Store whether each point is in the queue

// If there is a negative cycle, return true, otherwise return false.
bool spfa()
{
    // No need to initialize the dist array
    // Principle: If there is a shortest path with n points (excluding itself), then there are n+1 points including itself, and by the pigeonhole principle, there are at least two identical points, so there is a cycle.

    queue<int> q;
    for (int i = 1; i <= n; i ++ )
    {
        q.push(i);
        st[i] = true;
    }

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;       // If the shortest path from point 1 to x contains at least n points (excluding itself), it means there is a cycle
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return false;
}

3.6 Floyd Algorithm

—— Template Problem AcWing 854. Floyd Shortest Path Time complexity is O(n3), n represents the number of points

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Initialization:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// After the algorithm ends, d[a][b] represents the shortest distance from a to b
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

4 Minimum Spanning Tree

4.1 Naive Prim Algorithm

—— Template Problem AcWing 858. Prim Algorithm for Minimum Spanning Tree Time complexity is O(n2+m), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int n;      // n represents the number of points
int g[N][N];        // Adjacency matrix, store all edges
int dist[N];        // Store the distance from other points to the current minimum spanning tree
bool st[N];     // Store whether each point is already in the spanning tree


// If the graph is not connected, return INF (value is 0x3f3f3f3f), otherwise return the sum of the weights of the tree edges of the minimum spanning tree
int prim()
{
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

4.2 Kruskal Algorithm

—— Template Problem AcWing 859. Kruskal Algorithm for Minimum Spanning Tree Time complexity is O(mlogm), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int n, m;       // n is the number of points, m is the number of edges
int p[N];       // Parent node array of union-find

struct Edge     // Store edges
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

int find(int x)     // Core operation of union-find
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // Initialize union-find

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     // If the two connected components are not connected, merge the two connected components
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

5 Bipartite Graph

5.1 Coloring Method to Determine Bipartite Graph

—— Template Problem AcWing 860. Coloring Method to Determine Bipartite Graph Time complexity is O(n+m), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int n;      // n represents the number of points
int h[N], e[M], ne[M], idx;     // Adjacency list stores the graph
int color[N];       // Represents the color of each point, -1 means uncolored, 0 means white, 1 means black

// Parameters: u represents the current node, c represents the color of the current point
bool dfs(int u, int c)
{
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (color[j] == -1)
        {
            if (!dfs(j, !c)) return false;
        }
        else if (color[j] == c) return false;
    }

    return true;
}

bool check()
{
    memset(color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!dfs(i, 0))
            {
                flag = false;
                break;
            }
    return flag;
}

5.2 Hungarian Algorithm

—— Template Problem AcWing 861. Maximum Matching of Bipartite Graph Time complexity is O(nm), n represents the number of points, m represents the number of edges

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int n1, n2;     // n1 represents the number of points in the first set, n2 represents the number of points in the second set
int h[N], e[M], ne[M], idx;     // Adjacency list stores all edges, Hungarian algorithm only uses edges from the first set to the second set, so only one direction of edges is stored here
int match[N];       // Store which point in the first set each point in the second set is currently matched with
bool st[N];     // Indicates whether each point in the second set has been traversed

bool find(int x)
{
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true;
            if (match[j] == 0 || find(match[j]))
            {
                match[j] = x;
                return true;
            }
        }
    }

    return false;
}

// Find the maximum matching number, enumerate whether each point in the first set can be matched with a point in the second set
int res = 0;
for (int i = 1; i <= n1; i ++ )
{
    memset(st, false, sizeof st);
    if (find(i)) res ++ ;
}

Mathematical Knowledge

1 Prime Numbers

1.1 Trial Division to Determine Prime Numbers

—— Template Problem AcWing 866. Trial Division to Determine Prime Numbers

1
2
3
4
5
6
7
8
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;
}

1.2 Trial Division to Factorize Prime Numbers

—— Template Problem AcWing 867. Factorize Prime Numbers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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;
}

1.3 Naive Sieve Method to Find Prime Numbers

—— Template Problem AcWing 868. Sieve Prime Numbers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int primes[N], cnt;     // primes[] stores all prime numbers
bool st[N];         // st[x] stores whether x has been sieved

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

1.4 Linear Sieve Method to Find Prime Numbers

—— Template Problem AcWing 868. Sieve Prime Numbers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int primes[N], cnt;     // primes[] stores all prime numbers
bool st[N];         // st[x] stores whether x has been sieved

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

2 Divisors

2.1 Trial Division to Find All Divisors

—— Template Problem AcWing 869. Trial Division to Find Divisors

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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;
}

2.2 Number of Divisors and Sum of Divisors

—— Template Problem AcWing 870. Number of Divisors, AcWing 871. Sum of Divisors

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

2.3 Euclidean Algorithm

—— Template Problem AcWing 872. Greatest Common Divisor

1
2
3
4
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

3 Euler’s Function

3.1 Calculate Euler’s Function

—— Template Problem AcWing 873. Euler’s Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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;
}

3.2 Sieve Method to Calculate Euler’s Function

—— Template Problem AcWing 874. Sieve Method to Calculate Euler’s Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int primes[N], cnt;     // primes[] stores all prime numbers
int euler[N];           // Store the Euler's function of each number
bool st[N];         // st[x] stores whether x has been sieved


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

4 Fast Exponentiation

—— Template Problem AcWing 875. Fast Exponentiation Calculate m^k mod p, time complexity O(logk).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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;
}

5 Extended Euclidean Algorithm

—— Template Problem AcWing 877. Extended Euclidean Algorithm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Find x, y, such 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;
}

6 Gaussian Elimination

—— Template Problem AcWing 883. Gaussian Elimination to Solve Linear Equations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// a[N][N] is the 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 ++ )   // Find the row with the largest absolute value
            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]);      // Swap the row with the largest absolute value to the top
        for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];      // Make the leading coefficient of the current row 1
        for (int i = r + 1; i < n; i ++ )       // Use the current row to eliminate all columns below
            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; // No solution
        return 1; // Infinitely many 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; // Unique solution
}

7 Combinatorial Counting

7.1 Recursive Method to Calculate Combinations

—— Template Problem AcWing 885. Calculate Combinations I

1
2
3
4
5
// c[a][b] represents the number of ways to choose b from a apples
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;

7.2 Calculate Combinations by Preprocessing Inverse Elements

—— Template Problem AcWing 886. Calculate Combinations II First preprocess the remainder of all factorials fact[N], and the remainder of the inverse elements of all factorials infact[N] If the modulus is a prime number, the inverse element can be calculated using Fermat’s Little Theorem

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int qmi(int a, int k, int p)    // Fast exponentiation template
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

// Preprocess the remainder of factorials and the remainder of factorial inverse elements
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;
}

7.3 Lucas Theorem

—— Template Problem AcWing 887. Calculate Combinations III

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
If p is a prime number, for any integer 1 <= m <= n, there is:
    C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)

int qmi(int a, int k, int p)  // Fast exponentiation 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)  // Calculate the combination C(a, b) through the theorem
{
    if (a < b) return 0;

    LL x = 1, y = 1;  // x is the numerator, 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;
}

7.4 Factorization Method to Calculate Combinations

—— Template Problem AcWing 888. Calculate Combinations IV When we need to find the real value of the combination number, rather than the remainder of a certain number, the factorization method is more useful: 1. Use the sieve method to find all prime numbers within the range 2. Use the formula C(a, b) = a! / b! / (a - b)! to find the number of times each prime factor appears. The number of times p appears in n! is n / p + n / p^2 + n / p^3 + … 3. Use high-precision multiplication to multiply all prime factors

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int primes[N], cnt;     // Store all prime numbers
int sum[N];     // Store the number of times each prime number appears
bool st[N];     // Store whether each number has been sieved


void get_primes(int n)      // Linear sieve method to find prime numbers
{
    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)       // Find the number of times in n!
{
    int res = 0;
    while (n)
    {
        res += n / p;
        n /= p;
    }
    return res;
}


vector<int> mul(vector<int> a, int b)       // High-precision multiplication 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);  // Preprocess all prime numbers within the range

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

8 Game Theory

8.1 Catalan Numbers

—— Template Problem AcWing 889. 01 Sequence Satisfying Conditions Given n 0s and n 1s, they are arranged in a sequence of length 2n in a certain order, satisfying that the number of 0s in any prefix is not less than the number of 1s. The number of such sequences is: Cat(n) = C(2n, n) / (n + 1) NIM Game —— Template Problem AcWing 891. Nim Game Given N piles of items, the i-th pile has Ai items. Two players take turns to act, each time they can choose any pile and take away any number of items, they can take away the entire pile, but they cannot take nothing. The player who takes the last item wins. Both players adopt the optimal strategy, ask whether the first player is sure to win.

We call this game a NIM game. The state faced during the game is called a situation. The first player to act in the whole game is called the first player, and the second player to act is called the second player. If in a certain situation, no matter what action is taken, the game will be lost, then the situation is called a losing situation. The so-called optimal strategy means that if there is an action in a certain situation that makes the opponent face a losing situation after the action, then the action is preferred. At the same time, such a situation is called a winning situation. The game problems we discuss generally only consider the ideal situation, that is, both players make no mistakes and take optimal strategies when the game result.

The NIM game has no draw, only two situations: the first player is sure to win and the first player is sure to lose.

Theorem: The first player is sure to win in the NIM game if and only if A1 ^ A2 ^ … ^ An != 0

8.2 Fair Combinatorial Game ICG

If a game satisfies: Two players take turns to act; At any time during the game, the legal actions that can be performed are independent of which player it is; The player who cannot act loses; Then the game is called a fair combinatorial game. The NIM game is a fair combinatorial game, but urban construction chess games, such as Go, are not fair combinatorial games. Because the two sides of the Go game can only place black and white pieces respectively, and the victory and defeat judgment is also more complicated, it does not satisfy condition 2 and condition 3.

8.3 Directed Graph Game

Given a directed acyclic graph, there is a unique starting point in the graph, and a chess piece is placed on the starting point. Two players take turns to move the chess piece along the directed edge, each time moving one step, and the player who cannot move loses. This game is called a directed graph game. Any fair combinatorial game can be transformed into a directed graph game. The specific method is to regard each situation as a node in the graph, and connect directed edges from each situation to the next situation that can be reached by legal actions.

8.4 Mex Operation

Let S be a set of non-negative integers. Define mex(S) as the operation of finding the smallest non-negative integer that does not belong to the set S, that is: mex(S) = min{x}, x belongs to natural numbers, and x does not belong to S

8.5 SG Function

In a directed graph game, for each node x, let there be k directed edges from x, reaching nodes y1, y2, …, yk, define SG(x) as the result of the mex(S) operation on the set of SG function values of the successor nodes y1, y2, …, yk, that is: SG(x) = mex({SG(y1), SG(y2), …, SG(yk)}) In particular, the SG function value of the entire 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).

8.6 Sum of Directed Graph Games

—— Template Problem AcWing 893. Set-Nim Game Let G1, G2, …, Gm be m directed graph games. Define a directed graph game G, whose action rule is to choose any directed graph game Gi and act one step on Gi. G is called the sum of directed graph games G1, G2, …, Gm. The SG function value of the sum of directed graph games is equal to the XOR sum of the SG function values of each subgame it contains, that is: SG(G) = SG(G1) ^ SG(G2) ^ … ^ SG(Gm) Theorem A certain situation of a directed graph game is a winning situation if and only if the SG function value of the node corresponding to the situation is greater than 0. A certain situation of a directed graph game is a losing situation if and only if the SG function value of the node corresponding to the situation is equal to 0.

Data Range

The time limit for general ACM or written test questions is 1 second or 2 seconds. In this case, the number of operations in C++ code is best controlled within $10^7$∼$10^8$. The following gives the time complexity of the code and the algorithm selection under different data ranges: $n≤30$, Exponential level, dfs+pruning, state compression dp $n≤100=>O(n^3)$, floyd, dp, Gaussian elimination $n≤1000=>O(n^2)$, $O(n^{2}logn)$, dp, binary search, naive Dijkstra, naive Prim, Bellman-Ford $n≤10000=>O(n ·\sqrt{n})$, Block linked list, block, Mo’s algorithm $n≤100000=>O(nlogn)$=>Various sorts, segment tree, binary indexed tree, set/map, heap, topological sorting, dijkstra+heap, prim+heap, Kruskal, spfa, convex hull, half-plane intersection, binary search, CDQ divide and conquer, overall binary search, suffix array, tree chain decomposition, dynamic tree $n≤1000000=>O(n)$, And O(nlogn) algorithms with smaller constants=>Monotonic queue, hash, double pointer scan, union-find, kmp, AC automaton, O(nlogn) methods with smaller constants: sort, binary indexed tree, heap, dijkstra, spfa $n≤10000000=>O(n)$, Double pointer scan, kmp, AC automaton, linear sieve prime numbers $n≤10^9=>O(\sqrt n)$, Determine prime numbers $n≤10^{18}=>O(logn)$, Greatest common divisor, fast exponentiation, digit DP $n≤10^{1000}=>O((logn)^2)$, High-precision addition, subtraction, multiplication, division $n≤10^{100000}=>O(logk×loglogk)$, k represents the number of digits, FFT/NTT

Qingfeng’s Algorithm Homepage

Buy me a coffee~
Tim AlipayAlipay
Tim PayPalPayPal
Tim WeChat PayWeChat Pay
0%