ICPC 常用算法模板
flowchart LR ICPC---3["Search"] ICPC---4["Data Structure"] ICPC---5["Dynamic Programming"] ICPC---6["Number Theory、Linear Algebra"] ICPC---7["Combinatorics、Polynomial"] ICPC---8["Geometry"] ICPC---9["String"] ICPC---10["Graph Theory"]
目录
[TOC]
基本算法
尺取法/双指针
// 尺取法获取第k近的点下标
void getKth(int k) {
int l = 1, r = k + 1;
nxt[1] = k + 1;
for (int i = 2; i <= n; i++) {
while (r + 1 <= n && a[i] - a[l] > a[r + 1] - a[i])
l++, r++;
if (a[i] - a[l] < a[r] - a[i])
nxt[i] = r;
else
nxt[i] = l;
}
}void add(int x) { if (++mp[a[x]] == 1) cnt++; }
void del(int x) { if (--mp[a[x]] == 0) cnt--; }
// 尺取法,f[i] 表示区间 [i, f[i]] 中有k个不同的数
for (int l = 1, r = 1; l <= n; l++) {
while (cnt < k && r <= n)
add(r++);
if (cnt == k)
f[l] = r - 1;
else
f[l] = n + 1;
del(l);
}void add(int x) { sum += a[x]; }
void del(int x) { sum -= a[x]; }
sum = 0, res = n + 1;
// 查询满足区间和大于等于 s 的最短区间
for (int l = 1, r = 1; l <= n; l++) {
while (sum < s && r <= n)
add(r++);
if (sum < s)
break;
res = min(res, r - l);
del(l);
}二分和二分答案
int l, r, m; // [l,r)
while (r - l > 1)
if (check(m = (l + r) / 2))
l = m;
else
r = m;
// l : last true
// r : first false
// lower_bound(x) | check(mid) <x -> r |
// upper_bound(x) | check(mid)<=x -> r |倍增
快速幂式
// n个点跳跃m次位置
vector<ll> f(nxt);
for (; m; m >>= 1) {
if (m & 1)
for (int i = 1; i <= n; i++)
ans[i] = f[ans[i]]; //从上次的位置接着跳
vector<ll> ff(f);
for (int i = 1; i <= n; i++)
f[i] = ff[ff[i]]; //倍增跳
}朴素式
// 初始化
vector<vector<ll>> f(n + 1, vector<ll>(__lg(m) + 2));
for (int i = 1; i <= n; i++)
f[i][0] = nxt[i];
for (int t = 1; t <= __lg(m) + 1; t++)
for (int i = 1; i <= n; i++)
f[i][t] = f[f[i][t - 1]][t - 1];
// 查询
int p = 1;
for (int t = __lg(m) + 1; t >= 0; t--)
if (f[p][t] is 合法) {
p = f[p][t];
其他操作
}前缀和与差分
一维
前缀和:
差分:
差分区间修改:
二维
前缀和:
差分:
差分区间修改:
高维
前缀和:
- 容斥原理:
- SOS DP:
- 对于每一维度空间都是2的情况,有
差分:
树上
前缀和: 设 表示结点 到根节点的权值总和。
- 若是点权, 路径上的和为 。
- 若是边权, 路径上的和为 。
差分:
对于一次 的访问
- 点差分:
- 边差分:
离散化
将 数组离散化,逆映射保存在 中。
int compress(vector<pii> &a, vector<int> &d) {
for (auto &[l, r] : a)
d.push_back(l), d.push_back(r);
sort(d.begin(), d.end());
d.erase(unique(d.begin(), d.end()), d.end());
auto get = [&d](int x) { return lower_bound(d.begin(), d.end(), x) - d.begin(); };
for (auto &[l, r] : a)
l = get(l), r = get(r);
return d.size();
}
// 离散后修改
vector<int> f(compress(a, d));
for (auto &[l, r] : a)
fill(f.begin() + l, f.begin() + r, 1);
// 逆映射到离散前的原数值
for (int i = 0; i < f.size() - 1; i++)
if (f[i])
ans += d[i + 1] - d[i];二维的可以对两个维度分别离散化
排列和组合枚举
全排列
vector<int> a(n);
iota(a.begin(), a.end(), 1);
do {
// 判断排列的合法性
} while (next_permutation(a.begin(), a.end()));组合
int n = 4;
vector<int> a(n + 1);
for (int i = 0; i < (1 << n); i++) {
int cnt = __builtin_popcount(i);
a[cnt]++;
for (int j = 0; j < n; j++)
if (i >> j & 1) {
// 判断某位
}
}
for (int i = 0; i <= n; i++)
cout << a[i] << " ";数据结构
单调栈
维护某值的左边第一大,右边第一小
stack<int> s;
for (int i = 0; i < n; i++) {
while (!s.empty() && a[s.top()] < a[i]) {// 单调递减栈
// 此时a[i]是第一个 > a[s.top()]的数
s.pop();
}
// 此时a[i]左边有 s.size() 个 >= a[i] 的数
// 此时a[i]是第一个 <= a[s.top()]的数
s.push(i);
}单调队列
维护k区间最值
deque<int> q;
for (int i = 0; i < n; i++) {
while (!q.empty() && i - q.front() >= k) // 调整覆盖范围
q.pop_front();
while (!q.empty() && a[q.back()] < a[i]) // 保持单调递减
q.pop_back();
q.push_back(i);
if (i >= k - 1)
cout << a[q.front()] << endl; // 区间[i-k+1,i]最大值
}ST表/稀疏表/Sparse Table
维护区间最值
预处理:
查询:
template <typename T, T (*op)(T, T)> struct SparseTable {
int n, logn;
vector<vector<T>> dat;
SparseTable(const vector<T> &v) : n(v.size()), logn(__lg(n) + 1), dat(n + 1, vector<T>(logn + 1)) {
for (int i = 1; i <= n; i++)
dat[i][0] = v[i - 1];
for (int j = 1; j <= logn; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
dat[i][j] = op(dat[i][j - 1], dat[i + (1 << j - 1)][j - 1]);
}
T query(int l, int r) {
int s = __lg(r - l + 1);
return op(dat[l][s], dat[r - (1 << s) + 1][s]);
}
};
int maxn(int a, int b) { return a > b ? a : b; }并查集
维护集合的合并与查询是否相交
初始化:
查询/合并:
struct DisjointSet {
vector<int> p;
DisjointSet(int n = 1e6) : p(n) { iota(p.begin(), p.end(), 0); }
int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }
void merge(int x, int y) { p[find(y)] = find(x); }
};树状数组
维护具有区间减性质的序列,维护序列的前缀和信息
单点修改:
区间查询:
template <typename T> struct BIT {
int size;
vector<T> dat;
BIT(int n = 0) : size(n), dat(n + 1, 0) {}
inline int lowbit(int x) { return x & -x; }
void add(int i, T x) {
for (; i <= size; i += lowbit(i))
dat[i] += x;
}
T get(int i) {
T res = 0;
for (i = min(i, size); i; i -= lowbit(i))
res += dat[i];
return res;
}
vector<int> value() {
vector<int> val(size + 1);
for (int i = 1; i <= size; i++) val[i] = get(i);
for (int i = size; i >= 1; i--) val[i] -= val[i - 1];
return val;
}
T query(int l, int r) { return get(r) - get(l - 1); }
int kthLe(int k) {
int l = 0, r = size + 1, mid;
while (r - l > 1)
get(mid = (l + r) >> 1) < k ? l = mid : r = mid;
return r;
}
int kthGe(int k) {
int l = 0, r = size + 1, mid;
int sum = get(size);
while (r - l > 1)
sum - get(mid = (l + r) >> 1) < k ? r = mid : l = mid;
return r;
}
};线段树
维护“满足幺半群(封闭性;结合律;幺元)的性质的信息”的序列
如区间和、区间积、区间最大/小值、区间异或等可合并信息
zkw线段树
非递归实现,常数小,功能有限。
初始化建树:
单点修改:
区间查询:
template <typename T, T (*op)(T, T), T (*e)()> class SegmentTree {
int n;
vector<T> dat;
public:
SegmentTree(const int _n) : n(2 << __lg(_n - 1)), dat(n << 1, e()) {}
SegmentTree(const vector<T> &v) : n(2 << __lg(v.size() - 1)), dat(n << 1, e()) {
copy(v.begin(), v.end(), dat.begin() + n);
for (int p = n - 1; p; p--)
dat[p] = op(dat[p << 1], dat[p << 1 | 1]);
}
void update(int i, T k) { // update[i]=k
for (dat[i += n] = k; i; i >>= 1)
dat[i >> 1] = op(dat[i], dat[i ^ 1]);
}
T query(int i, int j) { // query[i,j]
T res = e();
for (i += n, j += n; i <= j; i >>= 1, j >>= 1) {
res = i & 1 ? op(res, dat[i++]) : res;
res = j & 1 ? res : op(res, dat[j--]);
}
return res;
}
};
template <typename T> T Add(T a, T b) { return a + b; }
template <typename T> T e() { return 0; }线段树
结构体实现,可扩展性强;lazy-tag 延迟更新。
维护 operator+ pushup apply pushdown 实现区间信息合并、标记与下传。
初始化建树:
区间修改:
区间查询:
单点修改
template <typename Info> struct SegmentTree {
int n;
vector<Info> info;
SegmentTree() : n(0) {}
SegmentTree(int n_) : n(n_), info(4 << __lg(n)) {}
SegmentTree(const vector<Info> &a) : SegmentTree(a.size()) {
function<void(int, int, int)> build = [&](int p, int l, int r) {
if (l == r)
return void(info[p] = a[l - 1]);
int m = (l + r) / 2;
build(p << 1, l, m);
build(p << 1 | 1, m + 1, r);
info[p] = info[p << 1] + info[p << 1 | 1];
};
build(1, 1, n);
}
void modify(int p, int l, int r, int i, const Info &v) {
if (i < l || r < i)
return;
if (l == r)
return void(info[p] = v);
int m = (l + r) / 2;
modify(p << 1, l, m, i, v);
modify(p << 1 | 1, m + 1, r, i, v);
info[p] = info[p << 1] + info[p << 1 | 1];
}
void modify(int x, const Info &v) { modify(1, 1, n, x, v); }
Info rangeQuery(int p, int l, int r, int a, int b) {
if (b < l || r < a)
return Info();
if (a <= l && r <= b)
return info[p];
int m = (l + r) / 2;
return rangeQuery(p << 1, l, m, a, b) + rangeQuery(p << 1 | 1, m + 1, r, a, b);
}
Info rangeQuery(int a, int b) { return rangeQuery(1, 1, n, a, b); }
};区间修改
template <typename Info, typename Tag> struct LazySegmentTree {
int n;
vector<Info> info;
vector<Tag> tag;
LazySegmentTree() : n(0) {}
LazySegmentTree(int n_) : n(n_), info(4 << __lg(n)), tag(4 << __lg(n)) {}
LazySegmentTree(const vector<Info> &a) : LazySegmentTree(a.size()) {
function<void(int, int, int)> build = [&](int p, int l, int r) {
if (l == r)
return void(info[p] = a[l - 1]);
int m = (l + r) / 2;
build(p << 1, l, m);
build(p << 1 | 1, m + 1, r);
info[p] = info[p << 1] + info[p << 1 | 1];
};
build(1, 1, n);
}
void apply(int p, int l, int r, const Tag &v) {
info[p].apply(l, r, v);
tag[p].apply(l, r, v);
}
void pushdown(int p, int l, int r) {
int m = (l + r) / 2;
apply(p << 1, l, m, tag[p]);
apply(p << 1 | 1, m + 1, r, tag[p]);
tag[p] = Tag();
}
void modify(int p, int l, int r, int x, const Info &v) {
if (x < l || r < x)
return;
if (l == r)
return void(info[p] = v);
pushdown(p, l, r);
int m = (l + r) / 2;
modify(p << 1, l, m, x, v);
modify(p << 1 | 1, m + 1, r, x, v);
info[p] = info[p << 1] + info[p << 1 | 1];
}
void modify(int x, const Info &v) { modify(1, 1, n, x, v); }
void rangeApply(int p, int l, int r, int a, int b, const Tag &v) {
if (b < l || r < a)
return;
if (a <= l && r <= b)
return apply(p, l, r, v);
pushdown(p, l, r);
int m = (l + r) / 2;
rangeApply(p << 1, l, m, a, b, v);
rangeApply(p << 1 | 1, m + 1, r, a, b, v);
info[p] = info[p << 1] + info[p << 1 | 1];
}
void rangeApply(int a, int b, const Tag &v) { rangeApply(1, 1, n, a, b, v); }
Info rangeQuery(int p, int l, int r, int a, int b) {
if (b < l || r < a)
return Info();
if (a <= l && r <= b)
return info[p];
pushdown(p, l, r);
int m = (l + r) / 2;
return rangeQuery(p << 1, l, m, a, b) + rangeQuery(p << 1 | 1, m + 1, r, a, b);
}
Info rangeQuery(int a, int b) { return rangeQuery(1, 1, n, a, b); }
};
struct Tag {
int x;
void apply(int l, int r, const Tag &v) { x += v.x; }
};
struct Info {
ll sum = 0;
void apply(int l, int r, const Tag &v) { sum += (r - l + 1) * v.x; }
friend Info operator+(Info a, Info b) { return Info{a.sum + b.sum}; }
};
// void modifty(int a, int b, T k, int p = 1) {
// if (a <= tr[p].l && tr[p].r <= b)
// return mark(k, p);
// pushdown(p);
// int mid = (tr[p].l + tr[p].r) >> 1;
// if (a <= mid) modifty(a, b, k, p << 1); // 左半区间与修改区间相交
// if (b > mid) modifty(a, b, k, p << 1 | 1); // 右半区间与修改区间相交
// tr[p] = tr[p << 1] + tr[p << 1 | 1];
// }
// Node query(int a, int b, int p = 1) {
// if (a <= tr[p].l && tr[p].r <= b)
// return tr[p];
// pushdown(p);
// int mid = (tr[p].l + tr[p].r) >> 1;
// if (b <= mid) return query(a, b, p << 1); // 只有左半区间与查询区间相交
// else if (a > mid) return query(a, b, p << 1 | 1); // 只有右半区间与查询区间相交
// else return query(a, b, p << 1) + query(a, b, p << 1 | 1); // (a <= mid < b) 两端区间都与查询区间相交
// }可持久化线段树/主席树
class SegmentTree {
struct Node {
int l = 0, r = 0, sum = 0;
};
vector<Node> tr;
vector<int> rt;
int n, cnt;
int update(int k, int d, int p, int l, int r) {
int q = tr.size();
tr.push_back(tr[p]);
if (l == r) {
tr[q].sum += d;
return q;
}
int mid = (l + r) / 2;
if (k <= mid)
tr[q].l = update(k, d, tr[p].l, l, mid);
else
tr[q].r = update(k, d, tr[p].r, mid + 1, r);
tr[q].sum = tr[tr[q].l].sum + tr[tr[q].r].sum;
return q;
}
int kth(int k, int p, int q, int l, int r) {
if (l == r)
return l;
int mid = (l + r) / 2;
int x = tr[tr[q].l].sum - tr[tr[p].l].sum;
if (x >= k)
return kth(k, tr[p].l, tr[q].l, l, mid);
else
return kth(k - x, tr[p].r, tr[q].r, mid + 1, r);
}
public:
SegmentTree(int n) : n(n), rt(1), cnt(1), tr(1) {}
void update(int k, int d) { rt.push_back(update(k, d, rt.back(), 1, n)); }
int kth(int k, int p, int q) { return kth(k, rt[p - 1], rt[q], 1, n); }
};分块与莫队算法
分块
template <typename T> class Block {
int n, t, m;
vector<int> l, r, id;
vector<T> &v, sum, tag;
void add(int p, int a, int b, T k) {
sum[p] += k * (b - a + 1);
for (int i = a; i <= b; i++)
v[i] += k;
}
T ask(int p, int a, int b) {
T res = tag[p] * (b - a + 1);
for (int i = a; i <= b; i++)
res += v[i];
return res;
}
public:
Block(vector<T> &v) // 1-index
: n(v.size() - 1), t(sqrt(n)), m(n / t + (n % t > 0)), l(m + 1), r(m + 1), id(n + 1),
v(v), sum(m + 1), tag(m + 1) {
for (int i = 1; i <= m; i++) {
l[i] = (i - 1) * t + 1, r[i] = min(i * t, n);
for (int j = l[i]; j <= r[i]; j++) {
sum[i] += v[j];
id[j] = (j - 1) / t + 1;
}
}
}
void update(int a, int b, T k) {
int p = id[a], q = id[b];
if (p == q) {
add(p, a, b, k);
} else {
for (int i = p + 1; i <= q - 1; i++)
tag[i] += k;
add(p, a, r[p], k);
add(q, l[q], b, k);
}
}
T query(int a, int b) {
T res = 0;
int p = id[a], q = id[b];
if (p == q) {
res += ask(p, a, b);
} else {
for (int i = p + 1; i <= q - 1; i++)
res += sum[i] + tag[i] * (r[i] - l[i] + 1);
res += ask(p, a, r[p]);
res += ask(q, l[q], b);
}
return res;
}
};二叉搜索树&平衡树
vector平衡树
template <typename T> struct BST {
vector<T> tr;
void insert(T x) { tr.insert(lower_bound(tr.begin(), tr.end(), x), x); }
void erase(T x) { tr.erase(lower_bound(tr.begin(), tr.end(), x)); }
int rank(T x) { return lower_bound(tr.begin(), tr.end(), x) - tr.begin() + 1; }
int kth(int x) { return tr.at(x - 1); }
int pre(int x) { return *prev(lower_bound(tr.begin(), tr.end(), x)); }
int nxt(int x) { return *upper_bound(tr.begin(), tr.end(), x); }
};图论
存图
邻接表/邻接矩阵
using Node = pair<int, int>;
vector<int> G[N];
int G[N][N]; //
int n, m;
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
G[v].push_back({u, w});
G[u][v] = G[v][u] = w;
}
for (int u = 0; u < n; u++)
for (auto &[v, w] : G[u]) {}
for (int v = 0; v < n; v++) if (G[u][v]) {}链式前向星
struct ChainForwardStar {
using Edge = pair<int, int>;
vector<int> head, next;
vector<Edge> edges;
int E;
ChainForwardStar(int V = 0) { head.resize(V + 1, -1); }
void addEdge(int u, int v, int w = 1) {
edges.push_back({v, w});
next.push_back(head[u]);
head[u] = E++;
}
};
ChainForwardStar G(n);
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
G.addEdge(u, v, w);
}
for (int u = 0; u < n; u++)
for (int i = G.head[u]; ~i; i = G.next[i]) {
auto &[v, w] = G.edges[i];
cout << u << " " << v << " " << w << endl;
}DFS/BFS
void dfs(int u) {
if (vis[u])
return;
vis[u] = true;
for (int v : G[u])
dfs(v);
}
void bfs(int u) {
queue<int> Q;
vis[u] = true;
Q.push(u);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int v : G[u])
if (!vis[v]) {
vis[v] = true;
Q.push(v);
}
}
}拓扑排序
vector<int> ans;
queue<int> q;
int indeg[N];
bool tsort() {
queue<int> q;
vector<int> ans;
for (int u = 1; u <= n; u++)
for (int v : G[u])
indeg[v]++;
for (int u = 1; u <= n; u++)
if (indeg[u] == 0)
q.push(u);
while (!q.empty()) {
int u = q.front();
q.pop();
ans.push_back(u);
for (int v : G[u])
if (--indeg[v] == 0)
q.push(v);
}
return ans.size() == n;
}最短路径
Dijkstra
朴素算法:
堆优化:
int dist[N];
bool vis[N];
void dijkstra(int s) {
memset(0, 0x3f, sizeof(dist));
dist[s] = 0;
while (true) {
int u = -1;
for (int i = 0; i < V; i++)
if ((u == -1 || dist[u] > dist[i]) && !vis[i])
u = i;
if (u == -1)
break;
vis[u] = true;
for (auto &[v, w] : G[u])
dist[v] = min(dist[v], dist[u] + w);
}
}
void dijkstra_heap(int s) {
priority_queue<Node, vector<Node>, greater<Node>> q;
memset(0, 0x3f, sizeof(dist));
dist[s] = 0;
q.push({0, s});
while (!q.empty()) {
auto [dist_u, u] = q.top();
q.pop();
if (dist[u] < dist_u)
continue;
for (auto &[v, w] : G[u])
if (dist[v] > dist[u] + w)
q.push({dist[v] = dist[u] + w, v});
}
}最小生成树
kurskal
vector<int> p;
int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }
int kruskal() {
int sum = 0, cnt = 0;
p.resize(V + 1);
iota(p.begin(), p.end(), 0);
sort(edges.begin(), edges.end());
for (auto &[u, v, w] : edges)
if (find(u) != find(v)) {
sum += w;
cnt++;
p[find(u)] = find(v);
}
return cnt == V - 1 ? sum : -1;
}LCA
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n;
vector<int> G[N];
int dep[N], p[N][20];
void dfs(int u, int fa) {
p[u][0] = fa;
for (int t = 1; (1 << t) <= dep[u]; t++)
p[u][t] = p[p[u][t - 1]][t - 1];
for (int v : G[u])
if (v != fa) {
dfs(v, u);
dep[v] = dep[u] + 1;
}
}
int LCA(int x, int y) {
if (dep[x] < dep[y])
swap(x, y);
for (int i = 19; i >= 0; i--)
if (dep[x] - (1 << i) >= dep[y])
x = p[x][i];
if (x == y)
return x;
for (int i = 19; i >= 0; i--)
if (p[x][i] != p[y][i])
x = p[x][i], y = p[x][i];
return p[x][0];
}
int main() {
cin >> n;
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
}数学
卷积
for (int k = al + bl - 2; k >= 0; k--) {
value_type tmp = 0;
for (int i = std::min(k, (int)al - 1); i >= 0 && k - i < bl; i--)
tmp += a[i] * b[k - i];
a[k] = tmp;
}位运算
快速幂
ll pow(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1, x = x * x)
if (n & 1)
s = s * x;
return s;
}素数
分解质因数/唯一分解定理
vector<int> getPrimeFactor(int n) {
vector<int> res;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
int cnt = 0;
while (n % i == 0)
n /= i, cnt++;
res.push_back(i);
}
if (n != 1)
res.push_back(n);
return res;
}素数测试
bool Prime(int x) {
for (int i = 2; i * i <= x; i++)
if (x % i == 0)
return false;
return x > 1;
}埃氏筛
vector<int> isPrime, primes;
void eratosthenes(int n) {
isPrime.assign(n + 1, 1);
primes.clear();
isPrime[0] = isPrime[1] = 0;
for (int i = 2; i <= n; i++)
if (isPrime[i]) {
primes.push_back(i);
for (int j = 2 * i; j <= n; j += i)
isPrime[j] = 0;
}
}线性筛
vector<int> minp, primes;
void sieve(int n) {
minp.assign(n + 1, 0);
primes.clear();
for (int i = 2; i <= n; i++) {
if (!minp[i])
minp[i] = i, primes.push_back(i);
for (int j = 0; i * primes[j] <= n; j++) {
minp[i * primes[j]] = primes[j];
if (i % primes[j] == 0)
break;
}
}
}欧几里得(Euclid)算法/辗转相除法
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
// c++14 : __gcd(a, b)
// c++17 : std::gcd(a, b) <numeric>扩展欧几里得算法
int extgcd(int a, int b, int &x, int &y) {
int d;
if (b == 0)
x = 1, y = 0, d = a;
else {
d = extgcd(b, a % b, y, x); // x' y'
y -= a / b * x; // y = y - a / b * x = x' - a / b * y' = y'' - a / b * y'
}
return d;
}数论分块
快速计算 , 其中 可以预处理前缀和
int f[N], g[N];
ll s[N];
ll H(int n) {
ll res = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
ll d = n / l;
r = n / d;
// res += (r - l + 1) * d;
res += (s[r] - s[l - 1]) * g[d];
}
return res;
}积性函数
如果有 , 那么
欧拉函数
欧拉函数,即 ,表示的是小于等于 和 互质的数的个数
当 是质数的时候,显然有
性质
- 欧拉函数是积性函数
- 若 , 其中 是质数,那么 (根据定义可知)
- 由唯一分解定理,设 , 其中 是质数,有
求一个数欧拉函数的值
int euler_phi(int n)
int ans = n;
for (int i = 2; i * i<= n; i++)
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}求多个数的欧拉函数值
void init_phi(int n) {
memset(isPrime, true, sizeof(isPrime));
isPrime[1] = false;
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
prime[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; j < cnt && ll(i) * prime[j] <= n; j++) {
isPrime[i * prime[j]] = false;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
} else {
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
}欧拉定理
若
费马小定理
若 为素数, ,则
另一个形式:对于任意整数 ,有
乘法逆元
如果一个线性同余方程 ,则 称为 的逆元,记作
扩展欧几里得法
int inv(int a, int b) {
int x, y;
extgcd(a, b, x, y);
return x;
}快速幂法
int inv(int a, int b) { return pow_mod(a, b - 2, b); }线性同余方程
线性同余方程
二元丢番图方程
// ax + by = c
bool liEu(int a, int b, int c, int &x, int &y) {
int d = extgcd(a, b, x, y);
if (c % d != 0)
return false;
x = 1LL * x * c / d;
y = 1LL * y * c / d;
return true;
}x的最小和最大值
void minmax(ll a, ll b, ll c, ll n) {
ll x0, y0, gcd = extgcd(a, b, x0, y0);
if (c < 0) {
cout << -1 << '\n';
return;
} else if (c == 0 && gcd == 0) {
cout << 0 << ' ' << n << '\n';
return;
} else if (c == 0) {
cout << 0 << ' ' << 0 << '\n';
return;
} else if (gcd == 0 || c % gcd != 0) {
cout << -1 << '\n';
return;
}
// c > 0, gcd != 0, c % gcd == 0
x0 *= c / gcd;
y0 *= c / gcd;
ll k_min = ceil(double(-x0) / (b / gcd));
ll k_max = floor(double(y0) / (a / gcd));
if (k_min > k_max) {
cout << -1 << '\n';
return;
}
ll x_min = 1e8, x_max = -1;
for (ll k = k_min; k <= k_max; k++) {
ll x_k = x0 + k * (b / gcd);
ll y_k = y0 - k * (a / gcd);
if (x_k + y_k <= n) {
x_min = min(x_min, x_k);
x_max = max(x_max, x_k);
}
}
cout << x_min << ' ' << x_max << '\n';
}中国剩余定理
LL CRT(int k, LL* a, LL* r) {
LL n = 1, ans = 0;
for (int i = 1; i <= k; i++) n = n * r[i];
for (int i = 1; i <= k; i++) {
LL m = n / r[i], b, y;
exgcd(m, r[i], b, y); // b * m mod r[i] = 1
ans = (ans + a[i] * m * b % n) % n;
}
return (ans % n + n) % n;
}模意义下的运算
template <typename T> T pow(T a, long long b) {
T s = 1;
for (; b; a = a * a, b >>= 1)
if (b & 1)
s = s * a;
return s;
}
struct Z {
long long x;
Z() {}
Z(long long x) : x(Norm(x)) {}
static long Norm(long long x) { return x %= mod, x < 0 ? x + mod : x; }
Z inv() const { return pow(*this, mod - 2); }
friend Z operator+(const Z &a, const Z &b) { return Z(a.x + b.x); }
friend Z operator-(const Z &a, const Z &b) { return Z(a.x - b.x); }
friend Z operator*(const Z &a, const Z &b) { return Z(a.x * b.x); }
friend Z operator/(const Z &a, const Z &b) { return a * b.inv(); }
friend ostream &operator<<(ostream &os, const Z &z) { return os << z.x; }
friend istream &operator>>(istream &is, Z &z) { return os >> z.x, z.x = Norm(z.x), is; }
};组合数学
排列数和组合数
struct Binom {
vector<Z> fac, ifac;
Binom(int n) : fac(n + 1), ifac(n + 1) {
fac[0] = ifac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
ifac[n] = 1 / fac[n];
for (int i = n - 1; i >= 1; i--)
ifac[i] = (i + 1) * ifac[i + 1];
}
Z C(int m, int n) { return (m < n || n < 0) ? 0 : fac[m] * ifac[n] * ifac[m - n]; }
Z P(int m, int n) { return (m < n || n < 0) ? 0 : fac[m] * ifac[m - n]; }
} math(N);线性代数
高斯消元
返回 0 无解,返回 1 有解,解为
// a:增广矩阵 n*(n+1)
int gauss(vector<vector<double>> &a, int n) {
for (int i = 0; i < n; i++) { // 枚举列
int r = i; // 该列最大数所在的行
for (int j = i + 1; j < n; j++)
if (abs(a[j][i]) > abs(a[r][i]))
r = j;
if (abs(a[r][i]) < eps)
return 0; // 列最大数为0,无解
swap(a[i], a[r]); // 把这一行移上来
for (int j = n; j >= i; j--) a[i][j] /= a[i][i]; // 这一行的主元系数变为1
for (int j = 0; j < n; j++) // 消去主元所在列的其他行的主元
if (j != i && abs(a[j][i]) > eps)
for (int k = n; k >= i; k--)
a[j][k] -= a[i][k] * a[j][i] / a[i][i];
}
return 1;
}
if (!gauss(a, n)) {
cout << "No Solution" << '\n';
} else {
for (int i = 0; i < n; i++)
printf("%.2lf\n", a[i][n]);
}多项式
FFT
using Poly = vector<int>;
using Complex = complex<double>;
const double eps = 0.49;
const double PI = acos(-1);
int rev[1 << 22];
void FFT(Complex a[], int n, int inv) {
for (int i = 0; i < n; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << __lg(n >> 1));
for (int i = 0; i < n; ++i)
if (i < rev[i])
swap(a[i], a[rev[i]]);
for (int i = 2; i <= n; i <<= 1) {
Complex wn(cos(2 * PI / i), inv * sin(2 * PI / i));
for (int j = 0; j < n; j += i) {
Complex w0(1, 0);
for (int k = j; k < j + i / 2; ++k, w0 *= wn) {
Complex x = a[k], y = w0 * a[k + i / 2];
a[k] = x + y;
a[k + i / 2] = x - y;
}
}
}
if (inv == -1)
for (int i = 0; i < n; i++)
a[i] /= n;
}
Poly operator*(Poly a, Poly b) {
int n = a.size() + b.size() - 1;
vector<Complex> c(2 << __lg(n - 1));
for (int i = 0; i < a.size(); i++)
c[i].real(a[i]);
for (int i = 0; i < b.size(); i++)
c[i].imag(b[i]);
FFT(c.data(), c.size(), 1);
for (int i = 0; i < c.size(); i++)
c[i] = c[i] * c[i];
FFT(c.data(), c.size(), -1);
Poly s(n);
for (int i = 0; i < n; i++)
s[i] = (int)(c[i].imag() / 2 + eps);
return s;
}字符串
Tire/字典树
struct Tire {
int nxt[100000][26], cnt;
bool exist[100000]; // 该结点结尾的字符串是否存在
void insert(string s) { // 插入字符串
int p = 0;
for (char c : s) {
c -= 'a';
if (!nxt[p][c])
nxt[p][c] = ++cnt; // 如果没有,就添加结点
p = nxt[p][c];
}
exist[p] = 1;
}
bool find(string s) { // 查找字符串
int p = 0;
for (char c : s) {
c -= 'a';
if (!nxt[p][c])
return false;
p = nxt[p][c];
}
return exist[p];
}
};# def point_in_polygon2(p: Point, polygon: Polygon):
# """判断点是否在多边形内部"""
# num = 0
# for edge in polygon.edges:
# u, v = edge.a, edge.b
# # 方法1:
# # if u.x < p.x and p.x <= v.x and point_line_relation(p, edge) == -1:
# # num ^= 1
# # if v.x < p.x and p.x <= u.x and point_line_relation(p, edge) == 1:
# # num ^= 1
# # 方法2:
# c = sgn(cross(p - u, v - u))
# if u.x < p.x and p.x <= v.x and c > 0:
# num += 1
# if v.x < p.x and p.x <= u.x and c < 0:
# num -= 1
# return num
// bool segmentInPolygon(Line l, vector<Vector> p) {
// int n = p.size();
// if (!pointInPolygon(l.a, p)) {
// return false;
// }
// if (!pointInPolygon(l.b, p)) {
// return false;
// }
// for (int i = 0; i < n; i++) {
// auto u = p[i];
// auto v = p[(i + 1) % n];
// auto w = p[(i + 2) % n];
// auto [t, p1, p2] = segmentIntersection(l, Line(u, v));
// if (t == 1) {
// return false;
// }
// if (t == 0) {
// continue;
// }
// if (t == 2) {
// if (pointOnSegment(v, l) && v != l.a && v != l.b) {
// if (((v - u) ^ (w - v)) > 0) {
// return false;
// }
// }
// } else {
// if (p1 != u && p1 != v) {
// if (pointOnLineLeft(l.a, Line(v, u)) || pointOnLineLeft(l.b, Line(v, u))) {
// return false;
// }
// } else if (p1 == v) {
// if (l.a == v) {
// if (pointOnLineLeft(u, l)) {
// if (pointOnLineLeft(w, l) && pointOnLineLeft(w, Line(u, v))) {
// return false;
// }
// } else {
// if (pointOnLineLeft(w, l) || pointOnLineLeft(w, Line(u, v))) {
// return false;
// }
// }
// } else if (l.b == v) {
// if (pointOnLineLeft(u, Line(l.b, l.a))) {
// if (pointOnLineLeft(w, Line(l.b, l.a)) && pointOnLineLeft(w, Line(u,
// v)))
// {
// return false;
// }
// } else {
// if (pointOnLineLeft(w, Line(l.b, l.a)) || pointOnLineLeft(w, Line(u,
// v)))
// {
// return false;
// }
// }
// } else {
// if (pointOnLineLeft(u, l)) {
// if (pointOnLineLeft(w, Line(l.b, l.a)) || pointOnLineLeft(w, Line(u,
// v)))
// {
// return false;
// }
// } else {
// if (pointOnLineLeft(w, l) || pointOnLineLeft(w, Line(u, v))) {
// return false;
// }
// }
// }
// }
// }
// }
// return true;
// }
// vector<Vector> hp(vector<Line> lines) {
// sort(lines.begin(), lines.end(), [&](const Line &l1, const Line &l2) {
// Vector d1 = l1.b - l1.a;
// Vector d2 = l2.b - l2.a;
// if (d1.toward() != d2.toward()) {
// return d1.toward() == 1;
// }
// return (d1 ^ d2) > 0;
// });
// deque<Line> ls;
// deque<Vector> ps;
// for (auto l :lines) {
// if (ls.empty()) {
// ls.push_back(l);
// continue;
// }
// while (!ps.empty() && !pointOnLineLeft(ps.back(), l)) {
// ps.pop_back();
// ls.pop_back();
// }
// while (!ps.empty() && !pointOnLineLeft(ps[0], l)) {
// ps.pop_front();
// ls.pop_front();
// }
// if (((l.b - l.a) ^ (ls.back().b - ls.back().a)) == 0) {
// if (((l.b - l.a) * (ls.back().b - ls.back().a)) > 0) {
// if (!pointOnLineLeft(ls.back().a, l)) {
// assert(ls.size() == 1);
// ls[0] = l;
// }
// continue;
// }
// return {};
// }
// ps.push_back(lineIntersection(ls.back(), l));
// ls.push_back(l);
// }
// while (!ps.empty() && !pointOnLineLeft(ps.back(), ls[0])) {
// ps.pop_back();
// ls.pop_back();
// }
// if (ls.size() <= 2) {
// return {};
// }
// ps.push_back(lineIntersection(ls[0], ls.back()));
// return vector(ps.begin(), ps.end());
// }