ACM算法模版

基础算法

矩阵快速幂

#include <cstdio>
#include <iostream>
#include <cstring>

const int N = 105;
const int MOD = 1e9 + 7;

using i64 = long long;

struct Matrix {
    int a[N][N];

    Matrix() {
        for (int i = 0; i < N; i++) a[i][i] = 1;
    }

    Matrix operator * (const Matrix &r) const {
        Matrix res;
        memset(res.a, 0, sizeof res.a);
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                int t = 0;
                for (int k = 0; k < N; k++) {
                    t += 1LL * a[i][k] * r.a[k][j] % MOD;
                    t = t % MOD;
                }
                res.a[i][j] = t;
            }
        }
        return res;
    }
};

Matrix FastPow_Matrix(Matrix a, i64 k) {
    Matrix res;
    while (k) {
        if (k & 1) res = a * res;
        k >>= 1;
        a = a * a;
    }
    return res;
}

Matrix a;

void solve() {
    int n;
    i64 k;
    scanf("%d %lld", &n, &k);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &a.a[i][j]);
        }
    }
    a = FastPow_Matrix(a, k);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d ", a.a[i][j]);
        }
        puts("");
    }
}

int main() {
    solve();
    return 0;
}

小数三分

#include <cstdio>
#include <cstring>
#include <iostream>

const int N = 20;

double coe[N];

double f(double x, int n) {
    double res = 0;
    for (int i = 0; i <= n; i++) {
        res = res * x + coe[i];
    }
    return res;
}

void solve() {
    int n;
    double l, r;
    scanf("%d %lf %lf", &n, &l, &r);
    for (int i = 0; i <= n; i++) {
        scanf("%lf", &coe[i]);
    }
    while (r - l > 1e-8) {
        double delta = (r - l) / 3.0;
        if (f(l + delta, n) < f(r - delta, n)) {
            l = l + delta;
        } else r = r - delta;
    }
    printf("%.7lf\n", r);
}

int main() {
    solve();
    return 0;
}

整数三分

#include <ctime>
#include <cstdlib>
#include <algorithm>
#include <iostream>

const int N = 2000;

long long a[N];

void solve() {
    srand(time(0));
    int m = rand() % N;
    for (int i = 1; i <= m; i++) {
        a[i] = rand() % 10 + a[i - 1];
    }
    for (int i = m + 1; i < N; i++) {
        a[i] = a[i - 1] - rand() % 10;
    }
    int l = 0, r = N - 1;
    while (r - l > 2) {
        int delta = (r - l) / 3;
        if (a[l + delta] < a[r - delta]) l = l + delta;
        else r = r - delta;
    }
    long long maxx = a[0];
    for (int i = l; i <= r; i++) {
        maxx = std::max(maxx, a[i]);
    }
}

int main() {
    solve();
    return 0;
}

二分搜索

#include <iostream>

const int N = 100005;

int q[N], n, k, T;

void solve() {
	std::cin >> k;
	int l = 0, r = n - 1;
	while (l < r) {
		int x = l + r >> 1;
		if (q[x] >= k) r = x;
		else l = x + 1;
	}
	if (q[l] != k) std::cout << "-1 -1" << std::endl;
	else {
		std::cout << l << " ";
		l = 0, r = n - 1;
		while (l < r) {
			int x = l + r + 1 >> 1;
			if (q[x] <= k) l = x;
			else r = x - 1;
		}
		std::cout << l << std::endl;
	}
}

int main() {
	std::cin >> n >> T;
	for (int i = 0; i < n; i++) std::cin >> q[i];
	while (T--) solve();
	return 0;
}

二维前缀和

#include <iostream>

const int N = 1005;

int a[N][N], s[N][N];

void solve() {
    int n, m, k;
    std::cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            std::cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }
    int x1, y1, x2, y2;
    while (k--) {
        std::cin >> x1 >> y1 >> x2 >> y2;
        std::cout << (s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]) << std::endl;
    }
}

int main() {
    solve();
    return 0;
}


高精度加减乘除比较

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

std::string str;

bool cmp(std::vector<int> A, std::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;
}


std::vector<int>toBigInt(std::string &s) {
	std::vector<int>res;
	for (int i = s.size() - 1; i >= 0; i--) res.push_back(s[i] - '0');
	return res;
}

std::vector<int>add(std::vector<int> A, std::vector<int> B) {
	std::vector<int> res;
	int t = 0;
	for (int i = 0; i < A.size() || i < B.size(); i++) {
		if (i < A.size()) t += A[i];
		if (i < B.size()) t += B[i];
		res.push_back(t % 10);
		t /= 10;
	}
	if (t) res.push_back(1);
	return res;
}

std::vector<int>sub(std::vector<int> A, std::vector<int> B) {
	std::vector<int> res;
	for (int i = 0, t = 0; i < A.size() || i < B.size(); i++) {
		t += A[i];
		if (i < B.size()) t -= B[i];
		res.push_back((t + 10) % 10);
		if (t < 0) t = -1;
		else t = 0;
	}
	while (res.size() > 1 && res.back() == 0) res.pop_back();
	return res;
}

std::vector<int>mul(std::vector<int> A, int b) {
	std::vector<int> res;
	for (int i = 0, t = 0; i < A.size() || t; i++) {
		if (i < A.size()) t += A[i] * b;
		res.push_back(t % 10);
		t /= 10;
	}
	while (res.size() > 1 && res.back() == 0) res.pop_back();
	return res;
}

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

void print(std::vector<int> &A) {
	for (int i = A.size() - 1; i >= 0; i--) std::cout << A[i];
}

void solve() {
	std::vector<int> A;
	int b;
	std::cin >> str >> b;
	A = toBigInt(str);
	int r = 0;
	std::vector<int> C = div(A, b, r);
	print(C);
	std::cout << std::endl << r << std::endl;
}

int main() {
	solve();
	return 0;
}

归并排序

#include <iostream>

const int N = 100005;

int q[N], n, tmp[N];

void mergeSort(int l, int r) {
	if (l == r) return;
	int x = l + r >> 1;
	mergeSort(l, x);
	mergeSort(x + 1, r);
	int i = l, j = x + 1, k = 0;
	while (i <= x && j <= r) {
		if (q[i] <= q[j]) tmp[k++] = q[i++];
		else tmp[k++] = q[j++];
	}
	while (i <= x) tmp[k++] = q[i++];
	while (j <= r) tmp[k++] = q[j++];
	for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}

void solve() {
	std::cin >> n;
	for (int i = 0; i < n; i++) std::cin >> q[i];
	mergeSort(0, n - 1);
	for (int i = 0; i < n; i++) std::cout << q[i] << " ";
	std::cout << std::endl;
}

int main() {
	solve();
	return 0;
}

归并排序求逆序对

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

const int N = 500005;

int a[N], tmp[N];
long long ans = 0;

void merge_sort(int *a, int l, int r) {
	if (l == r) return;
	int mid = l + r >> 1;
	merge_sort(a, l, mid);
	merge_sort(a, mid + 1, r);
	int x = l, y = mid + 1, tot = 0;
	while (x <= mid && y <= r) {
		if (a[x] <= a[y]) {
			tmp[tot++] = a[x];
			x++;
		} else {
			tmp[tot++] = a[y];
			ans = ans + mid - x + 1;
			y++;
		}
	}
	while (x <= mid) tmp[tot++] = a[x++];
	while (y <= r) tmp[tot++] = a[y++];
	for (int i = 0, j = l; i < tot; i++, j++) {
		a[j] = tmp[i];
	}
}

void solve() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
	}
	merge_sort(a, 0, n - 1);
	printf("%lld\n", ans);
}

int main() {
	solve();
	return 0;
}

枚举排列和子集生成

生成1-n的全排列

使用递归生成

定义一个函数void print_permutation(int n, int *A, int cur);,其中n为全排列中n的大小,A为生成的序列,cur为当前应该生成序列中的第几个数字。

递归的终止条件为:cur == n;每次递归如果没有终止,那么就枚举每个没有在A中出现的数字,将它填到A中并进行下一层递归。

#include <iostream>using namespace std;void print_permutation(int n, int *A, int cur) {    if (n == cur) {        for (int i = 0; i < n; i++) {            cout << A[i] << " ";        }        cout << '\n';        return;    }    for (int i = 1; i <= n; i++) {        bool flag = true;        for (int j = 0; j < cur; j++) {            if (A[j] == i) flag = false;        }        if (flag) {            A[cur] = i;            print_permutation(n, A, cur + 1);        }    }}void solve() {    int n;    cin >> n;    int A[23];    print_permutation(n, A, 0);}int main() {    solve();    return 0;}
使用STL中的next_permutation生成

STL中的next_permutation可以自动的找到字典序中序列的下一个排序。

#include <iostream>#include <algorithm>using namespace std;void print_permutation(int n) {    int a[23];    for (int i = 0; i < n; i++) {        a[i] = i + 1;    }    do {        for (int i = 0; i < n; i++) {            cout << a[i] << " ";        }        cout << '\n';    } while (next_permutation(a, a + n));}void solve() {    int n;    cin >> n;    print_permutation(n);}int main() {    solve();    return 0;}

生成可重集的排列

给出一组数字p,那么用p中的数字可以组合出多少不同的序列以及他们都是什么呢?注意p中的数字可能有重复。

#include <iostream>#include <algorithm>using namespace std;void print_permutation(int n, int *a, int *p, int cur) {    if (cur == n) {        for (int i = 0; i < n; i++) {            cout << a[i] << " ";        }        cout << '\n';        return;    }    for (int i = 0; i < n; i++) {        if (i && p[i] == p[i - 1]) continue;    // 防止出现重复        int c1 = 0, c2 = 0;        for (int j = 0; j < n; j++) {            if (p[j] == p[i]) c1++;        }        for (int j = 0; j < cur; j++) {            if (a[j] == p[i]) c2++;        }        if (c2 < c1) {      // 一个数字用的次数不能超过p序列中出现的次数            a[cur] = p[i];            print_permutation(n, a, p, cur + 1);        }    }}void solve() {    int n;    cin >> n;    int p[23], a[23];    for (int i = 0; i < n; i++) {        cin >> p[i];    }    sort(p, p + n);    print_permutation(n, a, p, 0);}int main() {    solve();    return 0;}

子集生成

这里要明确的是,枚举的是集合而不是排列。

递归进行增量构造

有点类似于选择排序算法,每次枚举从上一个枚举位置的数字的下一个数字枚举。

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

void print_subset(int n, int *A, int cur) {
    for (int i = 0; i < cur; i++) {
        cout << A[i] << " ";
    }
    cout << '\n';
    int s = cur == 0 ? 0 : A[cur - 1] + 1;
    for (int i = s; i <= n; i++) {
        A[cur] = i;
        print_subset(n, A, cur + 1);
    }
}

void solve() {
    int n;
    cin >> n;
    int A[23];
    print_subset(n, A, 0);
}

int main() {
    solve();
    return 0;
}
位向量法和二进制法

这两种都是通过标记每一位选还是不选来进行枚举的。

位向量法
#include <iostream>
#include <algorithm>
using namespace std;

void print_subset(int n, int *B, int cur) {
    if (cur == n + 1) {
        for (int i = 0; i <= n; i++) {
            if (B[i] == 1) cout << i << " ";
        }
        cout << endl;
        return;
    }
    B[cur] = 0;
    print_subset(n, B, cur + 1);
    B[cur] = 1;
    print_subset(n, B, cur + 1);
}

void solve() {
    int n;
    cin >> n;
    int B[23];
    print_subset(n, B, 0);
}

int main() {
    solve();
    return 0;
}
二进制法
#include <iostream>
#include <algorithm>
using namespace std;

void print_subset(int n) {
    for (int i = 0; i < (1 << n + 1); i++) {
        for (int j = 0; j <= n; j++) {
            if (i & (1 << j)) cout << j << " ";
        }
        cout << '\n';
    }
}

void solve() {
    int n;
    cin >> n;
    print_subset(n);
}

int main() {
    solve();
    return 0;
}

快速排序

#include <iostream>

const int N = 100005;

int q[N], n;

void qsort(int l, int r) {
	if (l == r) return;
	int x = q[l + r >> 1];
	int i = l - 1, j = r + 1;
	while (i < j) {
		while (q[++i] < x);
		while (q[--j] > x);
		if (i < j) std::swap(q[i], q[j]);
	}
	qsort(l, j);
	qsort(j + 1, r);
}

void solve() {
	std::cin >> n;
	for (int i = 0; i < n; i++) std::cin >> q[i];
	qsort(0, n - 1);
	for (int i = 0; i < n; i++) std::cout << q[i] << " ";
	std::cout << std::endl;
}

int main() {
	solve();
	return 0;
}

快速选择算法

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int N = 100005;int a[N];int quick_select(int a[], int l, int r, int k) {    if (l >= r) return a[l];            // !!!!    int i = l - 1, j = r + 1;    int mid = l + r >> 1;    mid = a[mid];    while (i < j) {        do i++; while (a[i] < mid);        do j--; while (a[j] > mid);        if (i < j) swap(a[i], a[j]);    }    if (k <= j + 1) return quick_select(a, l, j, k); // !!!!!    else return quick_select(a, j + 1, r, k);}void chant() {    int n, k;    scanf("%d %d", &n, &k);    for (int i = 0; i < n; i++) {        scanf("%d", &a[i]);    }    cout << quick_select(a, 0, n - 1, k) << endl;}int main() {    chant();    return 0;}

最长连续不重复子序列

#include <iostream>
#include <algorithm>

const int N = 100005;

int a[N], s[N], n;

void solve() {
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }
    int ans = 0;
    for (int i = 0, j = 0; i < n; i++) {
        s[a[i]]++;
        while (s[a[i]] > 1) {
            s[a[j]]--;
            j++;
        }
        ans = std::max(ans, i - j + 1);
    }
    std::cout << ans << std::endl;
}

int main() {
    solve();
    return 0;
}

判断子序列(双指针)

#include <iostream>

const int N = 100005;

int a[N], b[N];

void solve() {
    int n, m;
    std::cin >> n >> m;
    for (int i = 0; i < n; i++) std::cin >> a[i];
    for (int i = 0; i < m; i++) std::cin >> b[i];
    int i = 0, j = 0;
    while (i < n && j < m) {
        if (a[i] == b[j]) i++;
        j++;
    }
    if (i == n) puts("Yes");
    else puts("No");
}

int main() {
    solve();
    return 0;
}

一维差分

#include <iostream>const int N = 100005;int a[N], b[N];void insert(int l, int r, int c) {	b[l] += c;	b[r + 1] -= c;}void solve() {	int n, m;	std;:cin >> n >> m;	for (int i = 1; i <= n; i++) {		std::cin >> a[i];		insert(i, i, a[i]);	}	int l, r, c;	for (int i = 0; i < m; i++) {		std::cin >> l >> r >> c;		insert(l, r, c);	}	for (int i = 1; i <= n; i++) {		b[i] += b[i - 1];		std::cout << b[i] << " ";	}	return 0;}int main() {	solve();	return 0;}

二维差分

#include <iostream>
#include <cstring>

const int N = 1005;

int a[N][N];

void insert(int x1, int y1, int x2, int y2, int c) {
    a[x1][y1] += c;
    a[x1][y2 + 1] -= c;
    a[x2 + 1][y1] -= c;
    a[x2 + 1][y2 + 1] += c;
}

void solve() {
    int n, m, q, t;
    scanf("%d %d %d", &n, &m, &q);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf("%d", &t);
            insert(i, j, i, j, t);
        }
    }
    int x1, y1, x2, y2, c;
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d %d %d", &x1, &y1, &x2, &y2, &c);
        insert(x1, y1, x2, y2, c);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}

int main() {
    solve();
    return 0;
}

数据结构

扫描线

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

typedef long long LL;

const int N = 1000006;

struct scanLine {
    int x, y1, y2, val;

    scanLine(){}

    scanLine(int x, int y1, int y2, int val): x(x), y1(y1), y2(y2), val(val){}

    bool operator < (const scanLine &r) const {
        return x < r.x;
    }
} line[N];

vector<int> lisan;

struct segTree {
    int cnt, len;
} tree[N << 1];

int Find(int x) {
    return (int)(lower_bound(lisan.begin(), lisan.end(), x) - lisan.begin());
}

void push_up(int p, int l, int r) {
    if (tree[p].cnt) {
        tree[p].len = lisan[r + 1] - lisan[l];
    } else {
        tree[p].len = tree[p << 1].len + tree[p << 1 | 1].len;
    }
}

void update(int p, int l, int r, int ql, int qr, int val) {
    if (r < ql || l > qr) return;
    else if (l >= ql && r <= qr) {
        tree[p].cnt += val;
        push_up(p, l, r);
    } else {
        int mid = l + r >> 1;
        update(p << 1, l, mid, ql, qr, val);
        update(p << 1 | 1, mid + 1, r, ql, qr, val);
        push_up(p, l, r);
    }
}

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        line[i << 1] = scanLine(x1, y1, y2, 1);
        line[i << 1 | 1] = scanLine(x2, y1, y2, -1);
        lisan.push_back(y1);
        lisan.push_back(y2);
    }
    n <<= 1;
    sort(line, line + n);
    sort(lisan.begin(), lisan.end());
    lisan.erase(unique(lisan.begin(), lisan.end()), lisan.end());
    LL ans = 0;
    for (int i = 0; i < n - 1; i++) {
        int l = Find(line[i].y1);
        int r = Find(line[i].y2);
        int val = line[i].val;
        update(1, 0, lisan.size(), l, r - 1, val);
        ans += 1LL * tree[1].len * (line[i + 1].x - line[i].x);
    }
    printf("%lld\n", ans);
}

int main() {
    solve();
    return 0;
}

可持久化数组

#include <cstdio>#include <cstring>#include <vector>#include <map>#include <set>#include <queue>#include <iostream>const int N = 4000006;struct segTree {    int lson, rson, val;} tree[N << 2];int version[N], tot;void build_tree(int p, int l, int r) {    if (l == r) scanf("%d", &tree[p].val);    else {        int mid = l + r >> 1;        tree[p].lson = ++tot;        tree[p].rson = ++tot;        build_tree(tree[p].lson, l, mid);        build_tree(tree[p].rson, mid + 1, r);    }}int query(int p, int l, int r, int idx) {    if (l == r) return tree[p].val;    else {        int mid = l + r >> 1;        if (idx <= mid) return query(tree[p].lson, l, mid, idx);        else return query(tree[p].rson, mid + 1, r, idx);    }}void update(int p, int vp, int l, int r, int idx, int val) {    if (l == r) tree[p].val = val;    else {        tree[p] = tree[vp];        int mid = l + r >> 1;        if (idx <= mid) {            tree[p].lson = ++tot;            update(tree[p].lson, tree[vp].lson, l, mid, idx, val);        } else {            tree[p].rson = ++tot;            update(tree[p].rson, tree[vp].rson, mid + 1, r, idx, val);        }    }}void solve() {    int n, m;    scanf("%d %d", &n, &m);    build_tree(0, 1, n);    for (int i = 1; i <= m; i++) {        int v, op, pos, val;        scanf("%d %d %d", &v, &op, &pos);        if (op == 1) {            scanf("%d", &val);            version[i] = ++tot;            update(version[i], version[v], 1, n, pos, val);        } else {            version[i] = version[v];            int t = query(version[i], 1, n, pos);            printf("%d\n", t);        }    }//    for (int i = 0; i <= m; i++) {//        printf("%d: ", i);//        for (int j = 1; j <= n; j++) {//            printf("%d ", query(version[i], 1, n, j));//        }//        puts("");//    }}int main() {    solve();    return 0;}

树状数组

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

const int N = 10005;

int tree[N];

int lowbit(int x) { return x & -x; }

void update(int n, int idx, int val) {
    for (int i = idx; i <= n; i += lowbit(i)) tree[i] += val;
}

int query(int idx) {
    int res = 0;
    for (int i = idx; i; i -= lowbit(i)) res += tree[i];
    return res;
}

int query(int l, int r) {
    return query(r) - query(l - 1);
}

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; i++) {
        int t;
        scanf("%d", &t);
        update(n, i + 1, t);
    }
    for (int i = 0; i < m; i++) {
        int op, p, x;
        scanf("%d %d %d", &op, &p, &x);
        if (op == 0) { // update
            update(n, p, x);
        } else {
            printf("%d\n", query(p, x));
        }
    }
}

int main() {
    solve();
    return 0;
}

树状数组_区间修改

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

const int N = 10005;

int tree[N], n;

int lowbit(int x) {
    return x & -x;
}

void update(int p, int x) {
    for (int i = p; i <= n; i += lowbit(i)) {
        tree[i] += x;
    }
}

void update(int l, int r, int x) {
    update(l, x);
    update(r + 1, -x);
}

int query(int x) {
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) {
        res += tree[i];
    }
    return res;
}

void solve() {
    scanf("%d", &n);
    int m;
    cin >> m;
    for (int i = 0; i < m; i++) {
        int l, r, x;
        cin >> l >> r >> x;
        update(l, r, x);
    }
    for (int i = 1; i <= n; i++) {
        cout << query(i) << endl;
    }
}

int main() {
    solve();
    return 0;
}

kmp字符串匹配

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

const int N = 1000006;

char s[N], t[N];
int ne[N];

void solve() {
	scanf("%s %s", s + 1, t + 1);
	int n = strlen(s + 1), m = strlen(t + 1);
	for (int i = 2, j = 0; i <= m; i++) {
		while (j && t[j + 1] != t[i]) j = ne[j];
		if (t[j + 1] == t[i]) j++;
		ne[i] = j;
	}
	for (int i = 1, j = 0; i <= n; i++) {
		while (j && s[i] != t[j + 1]) j = ne[j];
		if (s[i] == t[j + 1]) j++;
		if (j == m) {
			printf("%d\n", i - j + 1);
		}
	}
	for (int i = 1; i <= m; i++) {
		printf("%d ", ne[i]);
	}
}

int main() {
	solve();
	return 0;
}

trie树字符串统计

#include <iostream>

const int N = 100005;

int son[N][26], cnt[N], cur, n;
char s[N], op[2];

void insert(char str[]) {
	int p = 0;
	for (int i = 0; str[i]; i++) {
		int x = str[i] - 'a';
		if (!son[p][x]) son[p][x] = ++cur;
		p = son[p][x];
	}
	cnt[p]++;
}

int query(char str[]) {
	int p = 0;
	for (int i = 0; str[i]; i++) {
		int x = str[i] - 'a';
		if (!son[p][x]) return 0;
		p = son[p][x];
	}
	return cnt[p];
}

void solve() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%s %s", op, s);
		if (op[0] == 'I') insert(s);
		else printf("%d\n", query(s));
	}
}

int main() {
	solve();
	return 0;
}

最大异或对

#include <iostream>const int N = 100005, M = 3000000;int n, a[N];int son[M][2], idx;void insert(int x) {	int p = 0;	for (int i = 30; ~i; i--) {		int &t = son[p][x >> i & 1];		if (!t) t = ++idx;		p = t;	}}int query(int x) {	int p = 0, res = 0;	for (int i = 30; ~i; i--) {		int t = (x >> i & 1);		if (son[p][!t]) {			res += 1 << i;			p = son[p][!t];		} else p = son[p][t];	}	return res;}void solve() {	scanf("%d", &n);	for (int i = 0; i < n; i++) {		scanf("%d", a + i);		insert(a[i]);	}	int res = 0;	for (int i = 0; i < n; i++) {		res = std::max(res, query(a[i]));	}	printf("%d\n", res);}int main() {	solve();	return 0;}

单调栈

#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
using namespace std;

const int N = 100005;

int a[N];

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", a + i);
    }
    stack<int> q;
    for (int i = 0; i < n; i++) {
        while (!q.empty() && q.top() >= a[i]) q.pop();
        if (q.empty()) printf("-1 ");
        else printf("%d ", q.top());
        q.push(a[i]);
    }
}

int main() {
    solve();
    return 0;
}

开放寻址法哈希

#include <iostream>
#include <cstring>

const int N = 200003, null = 0x3f3f3f3f;

int h[N];

int getHash(int x) {
	return ((x % N) + N) % N;
}

int Find(int x) {
	int k = getHash(x);
	while (h[k] != x && h[k] != null) k = (k + 1) % N;
	return k;
}

void solve() {
	memset(h, 0x3f, sizeof h);
	int n, x;
	char s[2];
	std::cin >> n;
	for (int i = 0; i < n; i++) {
		std::cin >> s >> x;
		int k = Find(x);
		if (*s == 'I') {
			h[k] = x;
		} else {
			if (h[k] == x) std::cout << "Yes" << std::endl;
			else std::cout << "No" << std::endl;
		}
	}
}

int main() {
	solve();
	return 0;
}

拉链法哈希

#include <iostream>
#include <cstring>

const int N = 100003;

int h[N], ne[N], val[N], idx;

int getHash(int x) {
	return ((x % N) + N) % N;
}

void insert(int x) {
	int k = getHash(x);
	val[idx] = x;
	ne[idx] = h[k];
	h[k] = idx++;
}

bool Find(int x) {
	int k = getHash(x);
	for (int i = h[k]; i != -1; i = ne[i]) {
		if (val[i] == x) return true;
	}
	return false;
}

void solve() {
	memset(h, -1, sizeof h);
	int n, x;
	char s[2];
	std::cin >> n;
	for (int i = 0; i < n; i++) {
		std::cin >> s >> x;
		if (*s == 'I') insert(x);
		else if (Find(x)) std::cout << "Yes" << std::endl;
		else std::cout << "No" << std::endl;
	}
}

int main() {
	solve();
	return 0;
}

滑动窗口

#include <iostream>const int N = 1000005;int q[N], a[N];void solve() {	int n, k;	scanf("%d %d", &n, &k);	for (int i = 0; i < n; i++) scanf("%d", &a[i]);	int head = 0, tail = -1;	for (int i = 0; i < n; i++) {		if (head <= tail && i - q[head] + 1 > k) head++;		while (head <= tail && a[q[tail]] >= a[i]) tail--; //		q[++tail] = i; //		if (i >= k - 1) printf("%d ", a[q[head]]);	}	puts("");	head = 0, tail = -1;	for (int i = 0; i < n; i++) {		if (head <= tail && i - q[head] + 1 > k) head++;		while (head <= tail && a[q[tail]] <= a[i]) tail--;		q[++tail] = i;		if (i >= k - 1) printf("%d ", a[q[head]]);	}	puts("");}int main() {	solve();	return 0;}

表达式求值(中缀表达式)

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

#define wa std::cerr << "----WARN----";

std::string s;
std::stack<int> num;
std::stack<char> op;

void eval() {
    int b = num.top(); num.pop();
    int a = num.top(); num.pop();
    char o = op.top(); op.pop();
    if (o == '+')      num.push(a + b);
    else if (o == '-') num.push(a - b);
    else if (o == '*') num.push(a * b);
    else if (o == '/') num.push(a / b);
}

int main() {
    std::unordered_map<char, int> pri;
    pri['+'] = pri['-'] = 1;
    pri['*'] = pri['/'] = 2;
    std::cin >> s;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') {
            op.push(s[i]);
        } else if (s[i] == ')') {
            while (op.top() != '(') eval();
            op.pop();
        } else if (isdigit(s[i])) {
            int t = 0, j = i;
            while (j < s.size() && isdigit(s[j])) {
                t = t * 10 + s[j++] - '0';
            }
            num.push(t);
            i = j - 1;
        } else {
            while (op.size() && pri[op.top()] >= pri[s[i]]) {
                eval();
            }
            op.push(s[i]);
        }
    }
    while (op.size()) eval();
    std::cout << num.top() << std::endl;
    return 0;
}

堆排序

#include <iostream>
#include <cstring>

#define wa std::cerr << "----WARN----" << std::endl;

const int N = 100005;

int h[N], size, n, m;

void down(int p) {
    int t = p;
    if (p * 2 <= size && h[p * 2] < h[t]) t = p * 2;
    if (p * 2 + 1 <= size && h[p * 2 + 1] < h[t]) t = p * 2 + 1;
    if (t != p) {
        std::swap(h[t], h[p]);
        down(t);
    }
}

void solve() {
    scanf("%d %d", &n, &m);
    size = n;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &h[i]);
    }
    for (int i = n / 2; i >= 1; i--) {
        down(i);
    }
    for (int i = 0; i < m; i++) {
        printf("%d ", h[1]);
        h[1] = h[size--];
        down(1);
    }
}

int main() {
    solve();
    return 0;
}

数据结构-堆

#include <cstdio>
#include <algorithm>
#include <iostream>

const int Maxn = 10005;

class Heap {
private:
    int heap[Maxn];
    int tot = 1;
    void percDown(int p) {
        int next = -1;
        if ((p << 1) < tot) {
            next = (p << 1);
            if ((p << 1 | 1) < tot && heap[p << 1 | 1] > heap[p << 1]) {
                next = (p << 1 | 1);
            }
        }
        if (next != -1 && heap[next] > heap[p]) {
            std::swap(heap[next], heap[p]);
            percDown(next);
        }
    }
    void percUp(int p) {
        if (p != 1 && heap[p >> 1] < heap[p]) {
            std::swap(heap[p >> 1], heap[p]);
            percUp(p >> 1);
        }
    }
public:
    bool insert(int val) {
        if (tot == Maxn - 1) return false;
        heap[tot++] = val;
        percUp(tot - 1);
        return true;
    }
    bool pop() {
        if (tot == 1) return false;
        heap[1] = heap[--tot];
        percDown(1);
        return true;
    }
    int top() {
        return tot == 1 ? -1 : heap[1];
    }
    bool empty() {
        return tot == 1;
    }
    int size() {
        return tot;
    }
};

int main() {
    Heap h;
    int n;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        int t;
        std::cin >> t;
        h.insert(t);
    }
    while (!h.empty()) {
        std::cout << h.top() << " ";
        h.pop();
    }
}

RMQ算法

#include <iostream>#include <cstdio>#include <cmath>#include <algorithm>using namespace std;const int N = 200005, M = 19;int f[N][M], a[N], n, m;void init() {    for (int j = 0; j < M; j++) {        for (int i = 0; i + (1 << j) - 1 < n; i++) {            if (j == 0) f[i][j] = a[i];            else {                f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);            }        }    }}int query(int l, int r) {    int len = r - l + 1;    int k = log(len) / log(2);    return max(f[l][k], f[r - (1 << k) + 1][k]);}void chant() {    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%d", &a[i]);    }    init();    scanf("%d", &m);    int l, r;    for (int i = 0; i < m; i++) {        scanf("%d %d", &l, &r);        l--, r--;        printf("%d\n", query(l, r));    }}int main() {    chant();    return 0;}

可持久化线段树(主席树)求区间第k小的数

#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
#include <set>
#include <queue>
#include <iostream>

const int N = 6000006;

class Lisan {
public:
    std::vector<int> lisan;

    void push(int x) {
        lisan.push_back(x);
    }

    void run() {
        std::sort(lisan.begin(), lisan.end());
        lisan.erase(std::unique(lisan.begin(), lisan.end()), lisan.end());
    }

    int Find(int x) {
        return (int)(std::lower_bound(lisan.begin(), lisan.end(), x) - lisan.begin()) + 1;
    }

    int get(int x) {
        return lisan[x - 1];
    }
} lisan;

struct segTree {
    int lson, rson, val;
} tree[N << 2];

int a[N], version[N], tot;

void push_up(int p) {
    tree[p].val = tree[tree[p].lson].val + tree[tree[p].rson].val;
}

void build_tree(int p, int l, int r) {
    if (l == r) {
        tree[p].val = 0;
    } else {
        int mid = l + r >> 1;
        tree[p].lson = ++tot;
        tree[p].rson = ++tot;
        build_tree(tree[p].lson, l, mid);
        build_tree(tree[p].rson, mid + 1, r);
        push_up(p);
    }
}

void update(int p, int vp, int l, int r, int idx) {
    tree[p] = tree[vp];
    if (l == r) {
        tree[p].val++;
    } else {
        int mid = l + r >> 1;
        if (idx <= mid) {
            tree[p].lson = ++tot;   // 对不一样的地方开新点
            update(tree[p].lson, tree[vp].lson, l, mid, idx);
        } else {
            tree[p].rson = ++tot;
            update(tree[p].rson, tree[vp].rson, mid + 1, r, idx);
        }
        push_up(p);
    }
}

int query(int u, int v, int l, int r, int k) {
    if (l == r) {
        return l;
    } else {
        int mid = l + r >> 1;
        int cl = tree[tree[v].lson].val - tree[tree[u].lson].val;
        if (k <= cl) {
            return query(tree[u].lson, tree[v].lson, l, mid, k);
        } else {
            return query(tree[u].rson, tree[v].rson, mid + 1, r, k - cl);
        }
    }
}

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        lisan.push(a[i]);
    }
    lisan.run();
    for (int i = 1; i <= n; i++) {
        version[i] = ++tot;
        int idx = lisan.Find(a[i]);
        update(version[i], version[i - 1], 1, n, idx);
    }
    for (int i = 0; i < m; i++) {
        int l, r, k;
        scanf("%d %d %d", &l, &r, &k);
        int t = query(version[l - 1], version[r], 1, n, k);
        t = lisan.get(t);
        printf("%d\n", t);
    }
}

int main() {
    solve();
    return 0;
}

ac自动机-简单版

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;

const int N = 1000006;

int tr[N << 2][26], idx;
int cnt[N << 2], fail[N << 2];
char s[N];

void insert() {
    int p = 0;
    for (int i = 0; s[i]; i++) {
        int c = s[i] - 'a';
        if (!tr[p][c]) tr[p][c] = ++idx;
        p = tr[p][c];
    }
    cnt[p] ++;
}

void build() {
    queue<int> q;
    for (int i = 0; i < 26; i++) {
        if (tr[0][i]) q.push(tr[0][i]);
    }
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < 26; i++) {
            int c = tr[u][i];
            if (!c) tr[u][i] = tr[fail[u]][i];
            else {
                fail[c] = tr[fail[u]][i];
                q.push(c);
            }
        }
    }
}

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s", s);
        insert();
    }
    build();
    scanf("%s", s);
    int ans = 0;
    for (int i = 0, j = 0; s[i]; i++) {
        int c = s[i] - 'a';
        j = tr[j][c];
        for (int t = j; t && cnt[t]; t = fail[t]) {
            ans += cnt[t];
            cnt[t] = 0;
        }
    }
    cout << ans << endl;
}

int main() {
    solve();
    return 0;
}

搜索与图论

prim算法求最段路

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

const int N = 200005;

struct EDGE {
    int v, w, next;
} e[N << 1];

int head[N], tot;

void add(int u, int v, int w) {
    e[tot].v = v;
    e[tot].w = w;
    e[tot].next = head[u];
    head[u] = tot++;
}

int dis[N], vis[N];

int prim(int n) {
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    int ans = 0;
    for (int i = 0; i < n; i++) {
        int u = -1, minn;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (u == -1 || minn > dis[j])) {
                u = j;
                minn = dis[j];
            }
        }
        if (minn == 0x3f3f3f3f) return -1;
        ans += minn;
        vis[u] = true;
        for (int j = head[u]; j != -1; j = e[j].next) {
            int v = e[j].v;
            int w = e[j].w;
            dis[v] = min(dis[v], w);
        }
    }
    return ans;
}

void solve() {
    memset(head, -1, sizeof head);
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    int ans = prim(n);
    if (ans == -1) puts("orz");
    else printf("%d\n", ans);
}

int main() {
    solve();
    return 0;
}

dfs求树的重心

#include <iostream>#include <cstring>#include <algorithm>const int N = 100005;const int INF = 0x3f3f3f3f;struct EDGE {	int v, next;} e[N << 1];int head[N], tot, n, ans = INF;bool vis[N];void add(int u, int v) {	e[tot].v = v;	e[tot].next = head[u];	head[u] = tot++;}int dfs(int u) {	vis[u] = true;	int sum = 1, maxx = 0;	for (int i = head[u]; i != -1; i = e[i].next) {		int v = e[i].v;		if (!vis[v]) {			int t = dfs(v);			sum += t;			maxx = std::max(maxx, t);		}	}	maxx = std::max(maxx, n - sum);	ans = std::min(ans, maxx);	return sum;}void solve() {	memset(head, -1, sizeof head);	std::cin >> n;	int u, v;	for (int i = 1; i < n; i++) {		std::cin >> u >> v;		add(u, v);		add(v, u);	}	dfs(1);	std::cout << ans << std::endl;}int main() {	solve();	return 0;}

匈牙利算法求二分图最大匹配

#include <iostream>
#include <cstring>

const int N = 505, M = 100005;

struct EDGE {
	int v, next;
} e[M];

int head[N], tot;
int match[N];
bool vis[N];

void add(int u, int v) {
	e[tot].v = v;
	e[tot].next = head[u];
	head[u] = tot++;
}

bool Find(int u) {
	for (int i = head[u]; i != -1; i = e[i].next) {
		int v = e[i].v;
		if (!vis[v]) {
			vis[v] = true; //
			if (match[v] == 0 || Find(match[v])) {
				match[v] = u;
				return true;
			}
		}
	}
	return false;
}

void solve() {
	memset(head, -1, sizeof head);
	int n1, n2, m;
	std::cin >> n1 >> n2 >> m;
	int u, v;
	for (int i = 0; i < m; i++) {
		std::cin >> u >> v;
		add(u, v);
	}
	int res = 0;
	for (int i = 1; i <= n1; i++) {
		memset(vis, 0, sizeof vis);
		if (Find(i)) res++;
	}
	std::cout << res << std::endl;
}

int main() {
	solve();
	return 0;
}

字符串哈希-求字符串中任意子串的哈希值

#include <iostream>

typedef unsigned long long ULL;

const int seed = 13331;
const int N = 100005;

int n, m;
char str[N];
ULL h[N], p[N];

ULL getHash(int l, int r) {
	return h[r] - h[l - 1] * p[r - l + 1];
}

void solve() {
	std::cin >> n >> m >> str + 1;
	p[0] = 1;
	for (int i = 1; i <= n; i++) {
		p[i] = p[i - 1] * seed;
		h[i] = h[i - 1] * seed + str[i];
	}
	int l1, r1, l2, r2;
	for (int i = 0; i < m; i++) {
		std::cin >> l1 >> r1 >> l2 >> r2;
		if (getHash(l1, r1) == getHash(l2, r2)) {
			puts("Yes");
		} else puts("No");
	}
}

int main() {
	solve();
	return 0;
}

拓扑排序

#include <iostream>#include <cstring>const int N = 100005;struct EDGE {	int v, next;} e[N];int head[N], tot;int n, m, d[N], q[N];void add(int u, int v) {	e[tot].v = v;	e[tot].next = head[u];	head[u] = tot++;}bool topSort() {	int hh = 0, tt = -1;	for (int i = 1; i <= n; i++) {		if (d[i] == 0) q[++tt] = i;	}	while (hh <= tt) {		int u = q[hh++];		for (int i = head[u]; i != -1; i = e[i].next) {			int v = e[i].v;			if (--d[v] == 0) q[++tt] = v;		}	}	return tt == n - 1;}void solve() {	memset(head, -1, sizeof head);	std::cin >> n >> m;	int u, v;	for (int i = 0; i < m; i++) {		std::cin >> u >> v;		add(u, v);		d[v]++;	}	if (topSort()) {		for (int i = 0; i < n; i++) {			std::cout << q[i] << " ";		}		puts("");	} else puts("-1");}int main() {	solve();	return 0;}

染色法判断二分图

#include <iostream>#include <cstring>const int N = 100005, M = 200005;struct EDGE {	int v, next;} e[M];int head[N], idx, color[N];void add(int u, int v) {	e[idx].v = v;	e[idx].next = head[u];	head[u] = idx++;}bool dfs(int u, int c) {	color[u] = c;	for (int i = head[u]; i != -1; i = e[i].next) {		int v = e[i].v;		if (!color[v]) {			if (!dfs(v, 3 - c)) return false;		} else if (color[v] == c) return false;	}	return true;}void solve() {	memset(head, -1, sizeof head);	int n, m;	std::cin >> n >> m;	int u, v;	for (int i = 0; i < m; i++) {		std::cin >> u >> v;		add(u, v);		add(v, u);	}	bool flag = true;	for (int i = 1; i <= n; i++) {		if (!color[i]) {			if (!dfs(i, 1)) {				flag = false;				break;			}		}	}	if (flag) puts("Yes");	else puts("No");}int main() {	solve();	return 0;}

spfa求最短路

#include <iostream>#include <queue>#include <cstring>const int N = 100005;const int INF = 0x3f3f3f3f;struct EDGE {    int v, w, next;} e[N << 1];int head[N], tot;int n, m;void add(int u, int v, int w) {    e[tot].v = v;    e[tot].w = w;    e[tot].next = head[u];    head[u] = tot++;}std::queue<int> q;int inq[N], dis[N];int spfa() {    memset(dis, 0x3f, sizeof dis);    dis[1] = 0;    q.push(1);    while (q.size()) {        int u = q.front();        q.pop();        inq[u] = false;        for (int i = head[u]; i != -1; i = e[i].next) {            int v = e[i].v;            int w = e[i].w;            if (dis[v] > dis[u] + w) {                dis[v] = dis[u] + w;                if (!inq[v]) {                    q.push(v);                    inq[v] = true;                }            }        }    }    return dis[n];}void solve() {    memset(head, -1, sizeof head);    std::cin >> n >> m;    int u, v, w;    for (int i = 0; i < m; i++) {        std::cin >> u >> v >> w;        add(u, v, w);    }    int ans = spfa();    if (ans == INF) puts("impossible");    else std::cout << ans << std::endl;}int main() {    solve();    return 0;}

floyd求最短路

#include <iostream>#include <cstring>#include <algorithm>const int N = 205;const int INF = 0x3f3f3f3f;int f[N][N];void solve() {    for (int i = 0; i < N; i++) {        for (int j = 0; j < N; j++) {            f[i][j] = (i == j ? 0 : INF);        }    }    int n, m, k;    std::cin >> n >> m >> k;    int u, v, w;    for (int i = 0; i < m; i++) {        std::cin >> u >> v >> w;        f[u][v] = std::min(f[u][v], w);    }    for (int k = 1; k <= n; k++) {        for (int i = 1; i <= n; i++) {            for (int j = 1; j <= n; j++) {                int t = f[i][k] + f[k][j];                f[i][j] = std::min(f[i][j], t);            }        }    }    for (int i = 0; i < k; i++) {        std::cin >> u >> v;        if (f[u][v] > INF / 2) {            puts("impossible");        } else {            std::cout << f[u][v] << std::endl;        }    }}int main() {    solve();    return 0;}

spfa判负环

#include <iostream>#include <queue>#include <cstring>const int N = 2005, M = 10005;const int INF = 0x3f3f3f3f;struct EDGE {    int v, w, ne;} e[M];int n, m;int head[N], tot;void add(int u, int v, int w) {    e[tot].v = v;    e[tot].w = w;    e[tot].ne = head[u];    head[u] = tot++;}int dis[N], inq[N], cnt[N];std::queue<int> q;bool spfa(int s) {    dis[s] = 0;    q.push(s);    inq[s] = 1;    cnt[s] = 1;    while (q.size()) {        int u = q.front();        q.pop();        inq[u] = false;        for (int i = head[u]; i != -1; i = e[i].ne) {            int v = e[i].v;            int w = e[i].w;            if (dis[v] > dis[u] + w) {                dis[v] = dis[u] + w;                if (!inq[v]) {                    inq[v] = true;                    q.push(v);                    if (++cnt[v] >= n) {                        return true;                    }                }            }        }    }    return false;}void solve() {    memset(head, -1, sizeof head);    memset(dis, INF, sizeof dis);    std::cin >> n >> m;    int u, v, w;    for (int i = 0; i < m; i++) {        std::cin >> u >> v >> w;        add(u, v, w);    }       for (int i = 1; i <= n; i++) {        if (dis[i] == INF && spfa(i)) {            puts("Yes");            return;        }    }    puts("No");}int main() {    solve();    return 0;}

数学知识

欧拉函数

#include <iostream>

void solve() {
    int n, t;
    std::cin >> n;
    while (n--) {
        std::cin >> t;
        int res = t;
        for (int i = 2; i <= t / i; i++) {
            if (t % i == 0) {
                while (t % i == 0) t /= i;
                res = res / i * (i - 1);
            }
        }
        if (t > 1) res = res / t * (t - 1);
        std::cout << res << std::endl;
    }
}

int main() {
    solve();
    return 0;
}

欧拉筛-线性筛

#include <iostream>
#include <algorithm>

const int N = 1000006;

int st[N], prime[N], tot;

int getPrime(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) prime[tot++] = i;
        for (int j = 0; j < tot && prime[j] <= n / i; j++) {
            st[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    return tot;
}

void solve() {
    int n;
    std::cin >> n;
    std::cout << getPrime(n) << std::endl;
}

int main() {
    solve();
    return 0;
}

分解质因数

#include <iostream>

void getDiv(int x) {
    for (int i = 2; i <= x / i; i++) {
        if (x % i == 0) {
            int s = 0;
            while (x % i == 0) {
                s++;
                x /= i;
            }
            std::cout << i << " " << s << std::endl;
        }
    }
    if (x > 1) std::cout << x << " " << 1 << std::endl;
    std::cout << std::endl;
}

void solve() {
    int n, t;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> t;
        getDiv(t);
    }
}

int main() {
    solve();
    return 0;
}

试除法求约数

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

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

void solve() {
    int n, t;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> t;
        std::vector<int> res = getDivs(t);
        for (int j = 0; j < res.size(); j++) {
            std::cout << res[j] << " ";
        }
        std::cout << std::endl;
    }
}

int main() {
    solve();
    return 0;
}

约数个数

#include <iostream>
#include <unordered_map>

typedef long long LL;

const LL MOD = 1e9 + 7;

std::unordered_map<LL, int> cnt;

void solve() {
    int n, t;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> t;
        for (int j = 2; j <= t / j; j++) {
            if (t % j == 0) {
                while (t % j == 0) {
                    cnt[j]++;
                    t /= j;
                }
            }
        }
        if (t > 1) cnt[t]++;
    }
    LL ans = 1;
    for (auto it : cnt) {
        ans = ans * (1 + it.second) % MOD;
    }
    std::cout << ans << std::endl;
}

int main() {
    solve();
    return 0;
}

约数之和

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

typedef long long LL;

const LL MOD = 1e9 + 7;

std::unordered_map<LL, int> cnt;

void solve() {
    int n;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        int t;
        std::cin >> t;
        for (int j = 2; j <= t / j; j++) {
            if (t % j == 0) {
                while (t % j == 0) {
                    cnt[j]++;
                    t /= j;
                }
            }
        }
        if (t > 1) {
            cnt[t]++;
        }
    }
    LL ans = 1;
    for (auto it : cnt) {
        int p = it.first;
        int e = it.second;
        LL t = 1;
        for (int i = 0; i < e; i++) {
            t = t * p + 1;
            t %= MOD;
        }
        ans = ans * t % MOD;
    }
    std::cout << ans << std::endl;
}

int main() {
    solve();
    return 0;
}

最大公约数(欧几里得算法)(辗转相除法)

#include <iostream>

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

void solve() {
    int n, a, b;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> a >> b;
        std::cout << gcd(a, b) << std::endl;
    }
}

int main() {
    solve();
    return 0;
}

筛法求欧拉函数

#include <iostream>

typedef long long LL;

const int N = 1000005;

int phi[N], p[N], st[N], tot;

LL getEulers(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            p[tot++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; p[j] <= n / i; j++) {
            st[i * p[j]] = true;
            if (i % p[j] == 0) {
                phi[i * p[j]] = phi[i] * p[j];
                break;
            }
            phi[i * p[j]] = phi[i] * (p[j] - 1);
        }
    }
    LL res = 0;
    for (int i = 0; i <= n; i++) {
        res = res + phi[i];
    }
    return res;
}

void solve() {
    int n;
    std::cin >> n;
    std::cout << getEulers(n) << std::endl;
}

int main() {
    solve();
    return 0;
}

快速幂求逆元

#include <iostream>

typedef long long LL;

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

void solve() {
    int n, a, p;
    std::cin >> n;
    for (int i = 0; i < n; i++) {
        std::cin >> a >> p;
        if (a % p == 0) puts("impossible");
        else std::cout << qpow(a, p - 2, p) << std::endl;
    }
}

int main() {
    solve();
    return 0;
}

扩展欧几里得算法

#include <iostream>
#include <cstdio>

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    } else {
        LL d = exgcd(b, a % b, y, x);
        y = y - a / b * x;
        return d;
    }
}

void solve() {
    int n, a, b;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &a, &b);
        LL x, y;
        exgcd(a, b, x, y);
        printf("%lld %lld\n", x, y);
    }
}

int main() {
    solve();
    return 0;
}

线性同余方程

#include <iostream>
#include <cstring>
 
typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    } else {
        LL d = exgcd(b, a % b, y, x);
        y = y - a / b * x;
        return d;
    }
}

void solve() {
    int n, a, b, m;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d %d", &a, &b, &m);
        LL x, y;
        LL d = exgcd(a, m, x, y);
        if (b % d != 0) {
            puts("impossible\n");
            continue;
        }
        printf("%lld\n", 1LL * x * b / d % m);
    }
}

int main() {
    solve();
    return 0;
}

组合数带取模模版

#include <cstdio>

typedef long long ll;

const int N = 100005;		// 最大能计算的n上限
const int MOD = 1000000007; // 1e9 + 7

ll fac[N]; // 用于存储阶乘结果

ll qpow(ll _a, ll _b) { // 快速幂
    ll res = 1, base = _a;
    while (_b) {
        if (_b & 1) res = res * base % MOD;
        base = base * base % MOD;
        _b >>= 1;
    }
    return res;
}

void init() {
	fac[0] = 1;
	for (int i = 1; i < N; i++) {
		fac[i] = fac[i - 1] * i % MOD;
	}
}

ll cal(ll n, ll m) {
    if (n < m) return 0;
    return 1LL * fac[n] * qpow(fac[m], MOD - 2) % MOD * qpow(fac[n - m], MOD - 2) % MOD;
}

int main() {
	init();
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		ll n, m;
		scanf("%lld %lld", &n, &m);
		ll res = cal(n, m);
		printf("%lld\n", res);
	}

	return 0;
}

FFT

#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;

const double Pi = acos(-1.0);
const int N = 1000006;

struct co {
    double a, b;

    co() {}

    co(double a, double b) : a(a), b(b) {}

    co operator+(const co &r) const {
        return co(a + r.a, b + r.b);
    }

    co operator-(const co &r) const {
        return co(a - r.a, b - r.b);
    }

    co operator*(const co &r) const {
        return co(a * r.a - b * r.b, a * r.b + b * r.a);
    }
} a[N << 2], b[N << 2], buf[N << 2];

void fft(co *a, int n, int type) {
    if (n == 1) return;
    int m = n >> 1;
    for (int i = 0; i < m; i++) {
        buf[i] = a[i << 1];
        buf[i + m] = a[i << 1 | 1];
    }
    memcpy(a, buf, sizeof(co) * (n + 1));
    co *a1 = a, *a2 = a + m;
    fft(a1, m, type);
    fft(a2, m, type);
    co wn = co(1, 0), u = co(cos(2 * Pi / n), type * sin(2 *Pi / n));
    for (int i = 0; i < m; i++) {
        co t = wn * a2[i];
        wn = wn * u;
        buf[i] = a1[i] + t;
        buf[i + m] = a1[i] - t;
    }
    memcpy(a, buf, sizeof(co) * (n + 1));
}

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i <= n; i++) {
        scanf("%lf", &a[i].a);
    }
    for (int i = 0; i <= m; i++) {
        scanf("%lf", &b[i].a);
    }
    int len = 1;
    while (len <= n + m) len <<= 1;
    fft(a, len, 1);
    fft(b, len, 1);
    for (int i = 0; i < len; i++) {
        a[i] = a[i] * b[i];
    }
    fft(a, len, -1);
    for (int i = 0; i <= n + m; i++) {
        printf("%d ", (int) (a[i].a / len + 0.5));
    }
}

int main() {
    solve();
    return 0;
}

动态规划

01背包

#include <iostream>
#include <algorithm>

const int N = 1005;

int v[N], w[N], f[N];

void solve() {
    int n, V;
    std::cin >> n >> V;
    for (int i = 1; i <= n; i++) std::cin >> v[i] >> w[i];
    for (int i = 1; i <= n; i++) {
        for (int j = V; j >= v[i]; j--) {
            f[j] = std::max(f[j], f[j - v[i]] + w[i]);
        }
    }
    std::cout << f[V];
}

int main() {
    solve();
    return 0;
}

完全背包

#include <iostream>
#include <algorithm>

const int N = 1005;

int v[N], w[N], f[N];

void solve() {
    int n, V;
    std::cin >> n >> V;
    for (int i = 1; i <= n; i++) std::cin >> v[i] >> w[i];
    for (int i = 1; i <= n; i++) {
        for (int j = v[i]; j <= V; j++) {
            f[j] = std::max(f[j], f[j - v[i]] + w[i]);
        }
    }
    std::cout << f[V] << std::endl;
}

int main() {
    solve();
    return 0;
}

多重背包-二进制优化

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

#define PII pair<int, int>
#define mp(a, b) make_pair(a, b)
#define fr first
#define sc second

const int N = 2005;

int dp[N];
std::vector<std::PII> a;

void solve() {
    int n, m;
    std::cin >> n >> m;
    for (int i = 0; i < n; i++) {
        int v, w, s;
        std::cin >> v >> w >> s;
        for (int k = 1; k <= s; k <<= 1) {
            s -= k;
            a.push_back(std::mp(v * k, w * k));
        }
        if (s) a.push_back(std::mp(v * s, w * s));
    }
    for (int i = 0; i < a.size(); i++) {
        int v = a[i].fr;
        int w = a[i].sc;
        for (int j = m; j >= v; j--) {
            dp[j] = std::max(dp[j], dp[j - v] + w);
        }
    }
    std::cout << dp[m] << std::endl;
}

int main() {
    solve();
    return 0;
}

分组背包

#include <iostream>
#include <algorithm>

const int N = 105;

int dp[N], v[N], w[N];

void solve() {
    int n, m, s;
    std::cin >> n >> m;
    for (int i = 0; i < n; i++) {
        std::cin >> s;
        for (int j = 0; j < s; j++) {
            std::cin >> v[j] >> w[j];
        }
        for (int j = m; j >= 0; j--) {
            for (int k = 0; k < s; k++) {
                if (v[k] > j) continue;
                dp[j] = std::max(dp[j], dp[j - v[k]] + w[k]);
            }
        }
    }
    std::cout << dp[m] << std::endl;
}

int main() {
    solve();
    return 0;
}

最长上升子序列

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

const int N = 100005;

int a[N], f[N];

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int t = 0;
    f[t] = -1;
    for (int i = 0; i < n; i++) {
        int l = 0, r = t;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (f[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        if (r == t) t++;
        f[r + 1] = a[i];
    }
    printf("%d\n", t);
}

int main() {
    solve();
    return 0;
}

区间dp-合并石子

#include <iostream>
#include <algorithm>

const int N = 305;
const int INF = 0x3f3f3f3f;

int n, s[N], f[N][N];

void solve() {
    std::cin >> n;
    for (int i = 1; i <= n; i++) {
        std::cin >> s[i];
    }
    for (int i = 2; i <= n; i++) {
        s[i] = s[i] + s[i - 1];
    }
    for (int len = 2; len <= n; len++) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int l = i, r = i + len - 1;
            f[l][r] = INF;
            for (int j = l; j < r; j++) {
                int t = f[l][j] + f[j + 1][r] + s[r] - s[l - 1];
                f[l][r] = std::min(f[l][r], t);
            }
        }
    }
    std::cout << f[1][n] << std::endl;
}

int main() {
    solve();
    return 0;
}

树形dp-没有上司的舞会

题目

某大学有\(n\)个职员,编号为 \(1,2...n\).

他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。

现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数\(r_i\),但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。

所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数.

思路

从根节点开始dfsdfs到叶节点设置叶节点的状态之后回溯转移。

状态\(f(i,j)\)表示以\(i\)点为根的子树,职员\(i\)来与不来能获得的最大快乐指数,其中\(j=0\)代表不来,\(j=1\)代表来。

叶节点的状态设置为\(f(i,0)=0,f(i,1)=a[i]\),其中\(a[i]\)为员工\(i\)能增加的快乐指数。

对于所有子节点都dfs过的点,其转移方程为:

\(f(i,1)=a[i] + \sum f(j,0)\)\(f(i,0)=\sum max\{f(j,0),f(j,1)\}\)

最终的答案就是根节点\(p\)参加与不参加的最大值。

代码

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

const int N = 6005;

struct EDGE { 
    int v, ne;
} e[N];

int h[N], tot, n;
int f[N][2], a[N], in[N];

void add(int u, int v) {
    e[tot].v = v;
    e[tot].ne = h[u];
    h[u] = tot++;
}

void dfs(int p) {
    f[p][1] = a[p];
    for (int i = h[p]; ~i; i = e[i].ne) {
        int v = e[i].v;
        dfs(v);
        f[p][1] += f[v][0];
        f[p][0] += max(f[v][0], f[v][1]);
    }
}

void solve() {
    memset(h, -1, sizeof h);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    int l, k; 
    for (int i = 0; i < n - 1; i++) {
        scanf("%d %d", &l, &k);
        add(k, l);
        in[l]++;
    }
    int p = 0;
    while (in[++p]);
    dfs(p);
    int ans = max(f[p][0], f[p][1]);
    printf("%d\n", ans);
}

int main() {
    solve();
    return 0;
}

记忆化搜索-滑雪

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

using namespace std;

const int N = 305;
const int dir[][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

int a[N][N], f[N][N];
int n, m;

bool check(int x, int y, int xx, int yy) {
    bool res = xx >= 0 && yy >= 0 && xx < n && yy < m;
    res = res && a[x][y] > a[xx][yy];
    return res;
}

int dfs(int x, int y) {
    if (f[x][y] != -1) {
        return f[x][y];
    }
    f[x][y] = 1;
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (check(x, y, xx, yy)) {
            f[x][y] = max(f[x][y], dfs(xx, yy) + 1);
        }
    }
    return f[x][y];
}

void solve() {
    memset(f, -1, sizeof f);
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    int ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            ans = max(ans, dfs(i, j));
        }
    }
    printf("%d\n", ans);
}

int main() {
    solve();
    return 0;
}

最长公共子序列

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

const int N = 1003;

char a[N], b[N];
int f[N][N];

void solve() {
    int n, m;
    scanf("%d %d", &n, &m);
    scanf("%s %s", a + 1, b + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
            if (a[i] == b[j]) f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + 1);
        }
    }
    printf("%d\n", f[n][m]);
}

int main() {
    solve();
    return 0;
}

posted @ 2021-10-01 18:38  牟翔宇  阅读(45)  评论(0编辑  收藏  举报