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\),但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。
所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数.
思路
从根节点开始dfs,dfs到叶节点设置叶节点的状态之后回溯转移。
状态\(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;
}