(整理)常用模板

1|0 算法模板
1|1 快读 & 快写
inline int read(){
int x = 0, f = 0; char ch = getchar();
while(!isdigit(ch)) f |= ch=='-', ch = getchar();
while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
template <typename T>
void print(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
}
2|0 基础算法
2|1 高精度
高精度加法
// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B){
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i ++){
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
return C;
}
高精度减法
// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B){
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++){
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
高精度乘低精度
// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b){
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++){
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
高精度除以低精度
// A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r){
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i -- ){
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
2|2 龟速乘
int lpow(int x, int y){
int res = 0;
while (y > 0) {
if(y & 1) {
res = (res + x) % mod;
}
y >>= 1;
x = (x + x) % mod;
}
return res;
}
2|3 二维前缀和
for(int i = 1; i <= N; i ++)
for(int j = 1; j <= N; j ++) {
c[i][j] = c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1] + c[i][j];
}
2|4 st表
O(nlog(n))预处理,O(1)查询,st(i, j) 数组表示下标 i 开始,长度为 2j 的区间(最大/最小值).
int st[N][22];
void init(int n){
for (int i = 1; i <= n; i ++) {
st[i][0] = a[i];
}
for (int j = 1; j <= 20; j ++) {
for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
st[i][j] = std::max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
}
}
}
int query (int l, int r) {
if(l > r) std::swap(l, r);
int k = std::log2(r - l + 1);
return std::max(st[l][k], st[r - (1 << k) + 1][k]);
}
2|5 二分
返回第一个
while(l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
**返回最后一个 **
while(l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
通俗的二分
while(l <= r) {
int mid = l + r >> 1;
if(check(mid)){
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
浮点二分
double bsearch(double l, double r){
double eps = 1e-7; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps){
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
1|0 求严格单调递增子序列长度
for(int i = 1; i <= n; i ++){
if(a[i] > f[cnt]) f[++ cnt] = a[i];
else{
int l = 1, r = cnt;
while(l < r){
int mid = l + r >> 1;
if(a[i] <= f[mid]) r = mid;
else l = mid + 1;
}
f[l] = a[i];
}
}
3|0 DP
3|1 背包问题
背包问题的三种情况的处理方式
- 体积最多是V,能获得的最大价值
memset(f, 0, sizeof f)
- 体积恰好是V,能获得的最少价值
memset(f, 0x3f, sizeof f), f[0] = 0
- 体积至少是V 能获得的最少价值,
memset(f, 0x3f, sizeof f), f[0] = 0, f[j] = min(f[max(0, j - v[i])], f[j])
3|2 最长公共子序列
for(int i = 1; i <= n; i ++) //第一个字符串
for(int j = 1; j <= n; j ++){ //第二个字符串
if(s1[i] == s2[j]) f[i][j] = f[i - 1][j - 1] + 1;
else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
另外两个序列都是全排列的话,就可以找映射关系来求最长上升子序列。
3|3 树上求最长路径
// d1 和 d2 分别是节点向叶子节点的最长和次长距离
int dfs(int u, int fa){
int dist = 0;
int d1 = 0, d2 = 0;
for(int i = head[u]; i; i = node[i].ne){
int j = node[i].to, w = node[i].w;
if(j == fa) continue;
int d = dfs(j, u) + w;
dist = max(dist, d);
if(d >= d1) d2 = d1, d1 = d;
else if(d >= d2) d2 = d;
}
ans = max(ans, d1 + d2);
return dist;
}
3|4 树形dp建虚树
int stk[N], dfn[N], top, col;
vector<int> g[N];
/*
LCA and dfn
*/
void in(int x){
if(top == 1) {
stk[++ top] = x;
return ;
}
int lca = LCA(stk[top], x);
if(lca == stk[top]){
/* stk[++ top] = x; (需要该lca的儿子) */
return ;
}
while(top > 1 && dfn[stk[top - 1]] >= dfn[lca]){
g[stk[top - 1]].push_back(stk[top]);
top --;
}
if(stk[top] != lca) {
g[lca].push_back(stk[top]), stk[top] = lca;
}
stk[++ top] = x;
}
int main(){
......
int k; cin >> k;
for(int i = 1; i <= k; i ++)
cin >> a[i];
sort(a + 1, a + k + 1, [&] (int x, int y){
return dfn[x] < dfn[y];
});
top = 0;
stk[++ top] = 1;
for(int i = 1; i <= k; i ++){
if(a[i] == 1) continue;
in(a[i]);
}
while(top > 1) {
g[stk[top - 1]].push_back(stk[top]);
-- top;
}
......
}
3|5 枚举子集
for (int i = x; i; i = (i - 1) & x)
4|0 数据结构
4|1 带size的并查集
struct DSU {
std::vector<int> p, sz;
DSU(int n) : p(n + 10), sz(n + 10, 1) { std::iota(p.begin(), p.end(), 0); }
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
void uni(int x, int y) {
if (find(x) != find(y)) {
sz[find(y)] += sz[find(x)];
}
p[find(x)] = find(y);
}
bool same(int x, int y) { return find(x) == find(y); }
int size(int x) { return sz[find(x)]; }
};
4|2 并查集判二分图
struct DSU {
std::vector<int> p;
DSU(int n) : p(n + 10) { std::iota(p.begin(), p.end(), 0); }
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
void uni(int x, int y) { p[find(x)] = find(y); }
bool same(int x, int y) { return find(x) == find(y); }
};
struct Edge { int u, v; } edge[M];
bool check(int n, int m) {
DSU dsu(n * 2);
for (int i = 1; i <= m; i ++) { //合并所有边的两个端点
int u = edge[i].u, v = edge[i].v;
dsu.uni(u, v + n), dsu.uni(u + n, v);
}
for (int i = 1; i <= n; i ++) {//判断是否有i与i+n在一个集合中
if (dsu.same(i, i + n)) {
return false;
}
}
return true;
}
4|3 单调队列
1|0 滑动窗口
// 常见模型:找出滑动窗口中的最大值/最小值
int q[N], hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
while (hh <= tt && check_out(q[hh])) hh ++ ; // 判断队头是否滑出窗口
while (hh <= tt && check(q[tt], i)) tt -- ;
q[++ tt] = i;
}
4|4 KMP
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
// 求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++){
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++){
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++;
if (j == m){
j = ne[j];
}
}
4|5 线段树 (二)
#include <bits/stdc++.h>
using i64 = long long;
struct SegmentTree {
const int n, p;
struct Node{
int l, r;
i64 sum, add, mul;
};
std::vector<Node> tr;
SegmentTree(int n, int p) : n(n), p(p), tr(4 << std::__lg(n)) {}
SegmentTree(std::vector<int> init, int x) : SegmentTree(init.size() - 1, x) {
std::function<void(int, int, int)> build = [&](int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
tr[u].mul = 1;
if(l == r) tr[u].sum = init[l];
else {
int mid = l + r >> 1;
build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
pushup(u);
}
};
build(1, 1, n);
}
void pushup(int u) {
tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p;
}
void eval(Node &t, i64 mul, i64 add) {
t.sum = (t.sum * mul % p + (t.r - t.l + 1) * add) % p;
t.mul = t.mul * mul % p;
t.add = (t.add * mul + add) % p;
}
void pushdown(int u) {
eval(tr[u << 1], tr[u].mul, tr[u].add);
eval(tr[u << 1 | 1], tr[u].mul, tr[u].add);
tr[u].mul = 1, tr[u].add = 0;
}
void modify(int u, int l, int r, i64 add, i64 mul) {
if(tr[u].l >= l && tr[u].r <= r){
eval(tr[u], mul, add);
} else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u << 1, l, r, add, mul);
if(r > mid) modify(u << 1 | 1, l, r, add, mul);
pushup(u);
}
}
i64 query(int u, int l, int r){
if(tr[u].r < l || tr[u].l > r) return 0;
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
return (query(u << 1, l, r) + query(u << 1 | 1, l, r)) % p;
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, p;
std::cin >> n >> m >> p;
std::vector<int> v(n + 1);
for(int i = 1; i <= n; i ++)
std::cin >> v[i];
SegmentTree tr(v, p);
while(m --) {
int op, l, r, d;
std::cin >> op >> l >> r;
if(op == 1) {
std::cin >> d;
tr.modify(1, l, r, 0, d);
} else if(op == 2) {
std::cin >> d;
tr.modify(1, l, r, d, 1);
} else std::cout << tr.query(1, l, r) << '\n';
}
return 0;
}
4|6 线段树合并
void merge (int &a, int b, int L, int R) {
if (!a || !b) {
a = (!b ? a : b);
return ;
}
if (L == R) {
/*区间更新*/
} else {
int mid = L + R >> 1;
merge (lson[a], lson[b], L, mid);
merge (rson[a], rson[b], mid + 1, R);
pushup (a);
}
}
4|7 splay
const int N = 100010;
int n, m;
struct Node
{
int s[2], p, v;
int size, flag;
void init(int _v, int _p)
{
v = _v, p = _p;
size = 1;
}
}tr[N];
int root, idx;
void pushup(int x)
{
tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
}
void pushdown(int x)
{
if (tr[x].flag)
{
swap(tr[x].s[0], tr[x].s[1]);
tr[tr[x].s[0]].flag ^= 1;
tr[tr[x].s[1]].flag ^= 1;
tr[x].flag = 0;
}
}
void rotate(int x)
{
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x; // k=0表示x是y的左儿子;k=1表示x是y的右儿子
tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k)
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k)
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
rotate(x);
}
if (!k) root = x;
}
void insert(int v)
{
int u = root, p = 0;
while (u) p = u, u = tr[u].s[v > tr[u].v];
u = ++ idx;
if (p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v, p);
splay(u, 0); //将插入节点旋转到根节点,保证时间复杂度
}
int get_k(int k)
{
int u = root;
while (true)
{
pushdown(u);
if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0];
else if (tr[tr[u].s[0]].size + 1 == k) return u;
else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1];
}
return -1;
}
void output(int u)
{
pushdown(u);
if (tr[u].s[0]) output(tr[u].s[0]);
if (tr[u].v >= 1 && tr[u].v <= n) printf("%d ", tr[u].v);
if (tr[u].s[1]) output(tr[u].s[1]);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i <= n + 1; i ++ ) insert(i);
while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
l = get_k(l), r = get_k(r + 2);
splay(l, 0), splay(r, l);
tr[tr[r].s[0]].flag ^= 1;
}
output(root);
return 0;
}
4|8 树链剖分
int n, idx, w[N];
int dfn[N], nw[N], top[N];
int son[N], fa[N], dep[N], sz[N];
std::vector<int> G[N];
struct Tree{
int l, r;
int add, sum;
}tr[N << 2];
void dfs1(int u, int father, int depth){
dep[u] = depth, fa[u] = father, sz[u] = 1;
for(auto v : G[u]){
if(v == father) continue;
dfs1(v, u, depth + 1);
sz[u] += sz[v];
if(sz[son[u]] < sz[v]) son[u] = v;
}
}
void dfs2(int u, int t){
dfn[u] = ++ idx, nw[idx] = w[u], top[u] = t;
if(!son[u]) return ;
dfs2(son[u], t);
for(auto v : G[u]){
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
void pushup(int u){
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u){
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if(root.add){
left.add += root.add, left.sum += (left.r - left.l + 1) * root.add;
right.add += root.add, right.sum += (right.r - right.l + 1) * root.add;
root.add = 0;
}
}
void build(int u, int l, int r){
tr[u] = {l, r, 0, nw[r]};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int k){
if(l <= tr[u].l && r >= tr[u].r){
tr[u].add += k;
tr[u].sum += k * (tr[u].r - tr[u].l + 1);
return ;
}
pushdown(u);
int mid = tr[u].r + tr[u].l >> 1;
if(l <= mid) update(u << 1, l, r, k);
if(r > mid) update(u << 1 | 1, l, r, k);
pushup(u);
}
int query(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) res += query(u << 1, l, r);
if(r > mid) res += query(u << 1 | 1, l, r);
return res;
}
void update_path(int u, int v, int k){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) std::swap(u, v);
update(1, dfn[top[u]], dfn[u], k);
u = fa[top[u]];
}
if(dep[u] < dep[v]) std::swap(u, v);
update(1, dfn[v], dfn[u], k);
}
void update_tree(int u, int k){
update(1, dfn[u], dfn[u] + sz[u] - 1, k);
}
int query_path(int u, int v){
int res = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) std::swap(u, v);
res += query(1, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if(dep[u] < dep[v]) std::swap(u, v);
res += query(1, dfn[v], dfn[u]);
return res;
}
int query_tree(int u){
return query(1, dfn[u], dfn[u] + sz[u] - 1);
}
4|9 树状数组
template <typename T>
struct BIT {
int n;
std::vector<T> a;
BIT(int n) : n(n), a(n + 1) {}
void add(int u, T v) {
for (int i = u; i <= n; i += i & -i) {
a[i] += v;
}
}
T sum(int u) {
T ans = 0;
for (int i = u; i > 0; i -= i & -i) {
ans += a[i];
}
return ans;
}
T rangeSum(int l, int r) {
return sum(r) - sum(l - 1);
}
};
template <typename T>
struct BIT {
int n;
std::vector<T> a;
BIT(int n) : n(n), a(n + 10) {}
void update(int u, T v) {
for (int i = u; i <= n; i += i & -i) {
a[i] = std::max(a[i], v);
}
}
T query(int u) {
T ans = 0;
for (int i = u; i > 0; i -= i & -i) {
ans = std::max(ans, a[i]);
}
return ans;
}
};
4|10 01字典树
int next[N << 4][2];
void insert (int x) {
int cur = 0;
for (int i = 31; i >= 0; i --) {
int bit = x >> i & 1;
if (!next[cur][bit]) {
next[cur][bit] = ++ idx;
}
cur = next[cur][bit];
}
num[cur] = x;
}
int query (int x) {
int cur = 0;
for (int i = 31; i >= 0; i --) {
int bit = x >> i & 1;
if (next[cur][bit ^ 1]) {
cur = next[cur][bit ^ 1];
} else {
cur = next[cur][bit];
}
}
return x ^ num[cur];
}
4|11 字符串哈希
核心思想:将字符串看成P进制数,P的进值是131或13331,取这两个值的冲突概率低(0.01%)
小技巧:取模的数用 264,这样直接用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ull;
ull h[N], p[N], P = 131; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++){
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ull get(int l, int r){
return h[r] - h[l - 1] * p[r - l + 1];
}
5|0 图论
5|1 LCA 求最近公共祖先
int depth[N], fa[N][25];
std::vector<int> G[N];
void dfs(int u, int father){
depth[u] = depth[father] + 1;
fa[u][0] = father;
for (int i = 1; i < 21; i ++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (auto v : G[u]) {
if (v == father) continue;
dfs (v, u);
}
}
int LCA(int a, int b){
if(depth[a] < depth[b]) std::swap(a, b);
for(int k = 20; k >= 0; k --)
if(depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if(a == b) return a;
for(int k = 20; k >= 0; k --)
if(fa[a][k] != fa[b][k])
a = fa[a][k], b = fa[b][k];
return fa[a][0];
}
5|2 dijkstra
std::vector<int> dis(n + 1, -1);
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> q;
q.push({0, 0});
while (!q.empty()) {
auto [d, u] = q.top();
q.pop();
if (dis[u] != -1) continue;
dis[u] = d;
for (auto &[v, w] : G[u]) {
q.push({d + w, v});
}
}
6|0 数学
6|1 线性筛
const int N = ?;
std::vector<int> is_primes(N + 1, 1);
is_primes[0] = is_primes[1] = 0;
std::vector<int> primes;
for (int i = 2; i <= N; i++) {
if (is_primes[i]) {
primes.push_back(i);
}
for (auto p : primes) {
if (1ll * i * p > N) break;
is_primes[i * p] = 0;
if (i % p == 0) break;
}
}
6|2 线性筛求莫比乌斯函数
const int N = ?;
std::vector<int> is_primes(N + 1, 1), mu(N + 1);
is_primes[0] = is_primes[1] = 0;
std::vector<int> primes;
mu[1] = 1;
for (int i = 2; i <= N; i ++) {
if (is_primes[i]) {
primes.push_back(i);
mu[i] = -1;
}
for (auto p : primes) {
if (1ll * i * p > N) break;
is_primes[i * p] = 0;
if (i % p == 0) break;
mu[p * i] = -mu[i];
}
}
std::vector<int> sum(N + 1);
for (int i = 1; i <= N; i ++) {
sum[i] = sum[i - 1] + mu[i];
}
6|3 欧拉函数
int phi (int a) {
int res = a;
for(int i = 2; i <= a / i; i ++) {
if(a % i == 0) {
res = res / i * (i - 1);
while(a % i == 0) a /= i;
}
}
if(a > 1) res = res / a * (a - 1);
return res;
}
筛法求欧拉函数
//int cnt = 0;
std::vector<int> euler(n + 1), st(n + 1), primes(n);
euler[1] = 1;
for(int i = 2; i <= n; i ++) {
if (!st[i]) {
primes[cnt ++] = i;
euler[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j ++) {
int t = primes[j] * i;
st[t] = 1;
if(i % primes[j] == 0) {
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
//--------------------------------------------------
const int N = 1000010;
std::vector<int> euler(N), is_primes(N, 1), primes;
void init (int n) {
euler[1] = 1;
is_primes[0] = is_primes[1] = 0;
for (int i = 2; i <= n; i ++) {
if (is_primes[i]) {
primes.push_back(i);
euler[i] = i - 1;
}
for (auto p : primes) {
if (1ll * i * p > n) break;
is_primes[i * p] = 0;
if (i % p == 0) {
euler[i * p] = euler[i] * p;
break ;
}
euler[i * p] = euler[i] * (p - 1);
}
}
}
6|4 康托展开
#include <bits/stdc++.h>
#define int long long
const int N = 1e6 + 10, mod = 998244353;
int num[N], fac[N];
void update (int x) {
for (int i = x; i < N; i += i & -i) {
num[i] ++;
}
}
int query (int x) {
int ans = 0;
for (int i = x; i > 0; i -= i & -i) {
ans += num[i];
}
return ans;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
fac[1] = 1;
for (int i = 2; i < N; i ++) {
fac[i] = fac[i - 1] * i % mod;
}
int n;
std::cin >> n;
std::vector<int> v(n + 1);
for (int i = 1; i <= n; i ++) {
std::cin >> v[i];
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
update (v[i]);
int cnt = v[i] - query (v[i]);
ans = ans + cnt * fac[n - i] % mod;
ans %= mod;
}
std::cout << ans + 1 << '\n';
return 0;
}
6|5 组合数
预处理逆元求组合数
int fact[N], infact[N];
int qmi (int a, int b, int p) {
int res = 1 % p; a %= p;
while (b > 0) {
if(b & 1) res = res * a % p;
a = a * a % p; b >>= 1;
}
return res;
}
void init (int n) {
fact[0] = infact[0] = 1;
for (int i = 1; i <= n; i ++) {
fact[i] = fact[i - 1] * i % mod;
infact[i] = qmi(fact[i], mod - 2, mod);
}
}
int C (int a, int b) {
//[ use init() ] and [long long]
if (a < 0 || b < 0 || a < b) return 0;
return fact[a] * infact[b] % mod * infact[a - b] % mod;
}
int P (int a, int b) {
if(a < b) return 0;
return fact[a] * infact[a - b] % mod;
}
//---------------------------------------
std::vector<Z> fac(n + 1), invfac(n + 1);
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i;
}
invfac[n] = fac[n].inv();
for (int i = n; i; i--) {
invfac[i - 1] = invfac[i] * i;
}
auto C = [&](int n, int m) -> Z {
if (n < m || m < 0) {
return 0;
}
return fac[n] * invfac[m] * invfac[n - m];
};
递推求组合数
int c[N][N];
for (int i = 0; i < N; i ++)
for (int j = 0; j <= i; j ++){
if (!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
一个底和高分别为a,b的一个直角三角形,他的斜边经过的整数点的个数是 gcd(a, b) + 1
6|6 卡特兰数
给定n个0和n个1,它们按照某种顺序排成长度为2n的序列,满足任意前缀中0的个数都不少于1的个数的序列的数量为: Cat(n) = C(2n, n) / (n + 1)
6|7 1 ~ x 与 y互质的个数
vector<int> p;
int f (int x, int y){
p.clear();
for (int i = 2; i * i <= y; i ++) {
if (y % i == 0) {
p.push_back(i);
while(y % i == 0) {
y /= i;
}
}
}
if (y > 1) {
p.push_back(y);
}
int ans = 0, cnt = p.size();
for (int i = 1; i < (1 << cnt); i ++) {
int tmp = 1, t = 0;
for (int j = 0; j < cnt; j ++) {
if (i >> j & 1) {
tmp *= p[j];
t ++;
}
}
ans += (t & 1 ? x / tmp : -x / tmp);
}
return x - ans;
}
6|8 扩展欧几里德求逆元
int exgcd (int a, int b, int &x, int &y) { // 返回的是gcd (a, b)
if (!b) {
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int inv (int a, int b) {
int x = 0, y = 0;
exgcd(a, b, x, y);
return (x % b + b) % b;
}
6|9 中国剩余定理
6|10 FFT
#include <bits/stdc++.h>
const int N = 3e5 + 10;
const double PI = acos(-1);
int n, m;
struct Complex {
double x, y;
Complex operator+ (const Complex& t) const {
return {x + t.x, y + t.y};
}
Complex operator- (const Complex& t) const {
return {x - t.x, y - t.y};
}
Complex operator* (const Complex& t) const {
return {x * t.x - y * t.y, x * t.y + y * t.x};
}
}a[N], b[N];
int rev[N], bit, tot;
void FFT (Complex a[], int inv) {
for (int i = 0; i < tot; i ++) {
if (i < rev[i]) {
std::swap (a[i], a[rev[i]]);
}
}
for (int mid = 1; mid < tot; mid <<= 1) {
auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
for (int i = 0; i < tot; i += mid * 2) {
auto wk = Complex({1, 0});
for (int j = 0; j < mid; j ++, wk = wk * w1) {
auto x = a[i + j], y = wk * a[i + j + mid];
a[i + j] = x + y, a[i + j + mid] = x - y;
}
}
}
}
int main() {
int n, m;
std::cin >> n >> m;
for (int i = 0; i <= n; i ++) {
std::cin >> a[i].x;
}
for (int i = 0; i <= m; i ++) {
std::cin >> b[i].x;
}
while ((1 << bit) < n + m + 1) {
bit ++;
}
tot = 1 << bit;
for (int i = 0; i < tot; i ++) {
rev[i] = (rev[i >> 1] >> 1) | (i & 1) << (bit - 1);
}
FFT(a, 1), FFT(b, 1);
for (int i = 0; i < tot; i ++) {
a[i] = a[i] * b[i];
}
FFT(a, -1);
for (int i = 0; i <= n + m; i ++) {
std::cout << int(a[i].x / tot + 0.5) << ' ';
}
return 0;
}
6|11 狄利克雷卷积
定义两个函数f, g 的狄利克雷卷积为 f∗g , 其自成一个函数。
有:(f∗g)(n)=∑d|nf(d)g(nd)
6|12 杜教筛
g(1)S(n)=n∑i=1(f∗g)(i)−n∑i=2g(i)S(⌊ni⌋)
给定一个正整数,求
ans1=n∑i=1φ(i)
ans2=n∑i=1μ(i)
#include <bits/stdc++.h>
#define int long long
const int N = 2e6 + 10;
int mu[N], sum_mu[N];
std::vector<int> primes, is_primes(N, 1);
std::map<int, int> Mu;
int sMu (int n) {
if (n < N) return sum_mu[n];
if (Mu.count (n)) return Mu[n];
int res = 1;
for (int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= (r - l + 1) * sMu (n / l);
}
return Mu[n] = res;
}
int sPhi (int n) {
int res = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res += (sMu(r) - sMu(l - 1)) * (n / l) * (n / l);
}
return (res - 1) / 2 + 1;
}
void init (int n) {
mu[1] = 1;
is_primes[0] = is_primes[1] = 0;
for (int i = 2; i <= n; i ++) {
if (is_primes[i]) {
primes.push_back(i);
mu[i] = -1;
}
for (auto p : primes) {
if (1ll * i * p > n) break;
is_primes[i * p] = 0;
if (i % p == 0) break ;
mu[i * p] = -mu[i];
}
}
for (int i = 1; i <= n; i ++) {
sum_mu[i] = sum_mu[i - 1] + mu[i];
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
init (2000000);
int _;
std::cin >> _;
while (_ --) {
int n;
std::cin >> n;
std::cout << sPhi (n) << ' ' << sMu (n) << '\n';
}
return 0;
}
6|13 判凸包
int n, stk[N], top;
bool st[N];
struct Point {
double x, y;
bool operator < (const Point &t) const {
if(x != t.x) return x < t.x;
return y < t.y;
}
Point operator - (const Point &t) const {
return (Point){x - t.x, y - t.y};
}
};
Point q[N];
double get_dist (Point a, Point b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
double cross (Point a, Point b) {
return a.x * b.y - a.y * b.x;
}
double area (Point a, Point b, Point c) {
return cross(b - a, c - a);
}
bool andrew() {
std::sort(q, q + n);
top = 0;
for (int i = 0 ; i < n; i ++) {
while(top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) {
if(area(q[stk[top - 1]], q[stk[top]], q[i]) < 0) {
st[stk[top -- ]] = false;
} else {
top --;
}
}
stk[++ top] = i;
st[i] = true;
}
st[0] = false;
for (int i = n - 1; i >= 0; i --) {
if (st[i]) continue;
while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) {
top --;
}
stk[++ top] = i;
}
return top == 5;
}
7|0 小知识
7|1 cf的保护分
前六场初始分:Promotions of the displayed rating will be equal to 500,350,250,150,100,50 (in total exactly 1400).
7|2 数据范围所能使用的时间复杂度
7|3 cf 防止哈希被卡
struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
unordered_map<int, int, custom_hash> safe_map;
//参考链接https://codeforces.com/blog/entry/62393
7|4 能用PII作哈希的键值
struct hashfunc{
template<typename T, typename U>
size_t operator() (const pair<T, U> &i) const{
return hash<T>()(i.first) ^ hash<U>()(i.second);
}
};
7|5 Debug
std::string to_string(std::string s) { return '"' + s + '"'; }
std::string to_string(char s) { return std::string(1, s); }
std::string to_string(const char* s) { return to_string((std::string)s); }
std::string to_string(bool b) { return (b ? "true" : "false"); }
template <typename A>
std::string to_string(A b) { return std::to_string(b); }
template <typename A, typename B>
std::string to_string(std::pair<A, B> p) {
return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}
template <typename A>
std::string to_string(std::vector<A> v) {
bool f = 1;
std::string r = "{";
for (auto& x : v) {
if (!f) r += ", ";
f = 0;
r += to_string(x);
}
return r + "}";
}
template <typename A, typename B>
std::string to_string(std::map<A, B> map) {
bool f = 1;
std::string r = "{";
for (auto& [x, y] : map) {
if (!f) r += ", ";
f = 0;
r += to_string(x) + ": " + to_string(y);
}
return r + "}";
}
template <typename A>
std::string to_string(std::multiset<A> set) {
bool f = 1;
std::string r = "{";
for (auto& x : set) {
if (!f) r += ", ";
f = 0;
r += to_string(x);
}
return r + "}";
}
template <typename A>
std::string to_string(std::set<A> set) {
bool f = 1;
std::string r = "{";
for (auto& x : set) {
if (!f) r += ", ";
f = 0;
r += to_string(x);
}
return r + "}";
}
void debug_out() { std::cout << '\n'; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
std::cout << " " << to_string(H);
debug_out(T...);
}
#define pr(...) std::cout << "[" << #__VA_ARGS__ << "] :", debug_out(__VA_ARGS__)
#define dearr(arr, a, b) \
std::cout << #arr << " : "; \
for (int i = a; i <= b; i ++) \
std::cout << arr[i] << " "; \
std::cout << '\n';
#define demat(mat, row, col) \
std::cout << #mat << " :\n"; \
for (int i = 1; i <= row; i++) { \
std::cout << i << " : \n"; \
for (int j = 1; j <= col; j++) \
std::cout << mat[i][j] << " "; \
std::cout << '\n'; \
}
7|6 取模int
using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
template <int P>
struct MInt {
int x;
constexpr MInt() : x{} {}
constexpr MInt(i64 x) : x{norm(x % getMod())} {}
static int Mod;
constexpr static int getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(int Mod_) {
Mod = Mod_;
}
constexpr int norm(int x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr int val() const {
return x;
}
explicit constexpr operator int() const {
return x;
}
constexpr MInt operator-() const {
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv() const {
assert(x != 0);
return power(*this, getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs) & {
x = 1LL * x * rhs.x % getMod();
return *this;
}
constexpr MInt &operator+=(MInt rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs) & {
return *this *= rhs.inv();
}
friend constexpr MInt operator*(MInt lhs, MInt rhs) {
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs, MInt rhs) {
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs, MInt rhs) {
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs, MInt rhs) {
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
i64 v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
return os << a.val();
}
friend constexpr bool operator==(MInt lhs, MInt rhs) {
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MInt lhs, MInt rhs) {
return lhs.val() != rhs.val();
}
};
using Z = MInt<998244353>;
7|7 臭氧优化
#pragma GCC optimize(3,"Ofast","inline")
7|8 C++ STL简介
priority_queue, 优先队列,默认是大顶堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小顶堆的方式:priority_queue<int, vector<int>, greater<int>> q;
stack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素
deque, 双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
bitset,
bitset<10000> s;
~, &, |, ^
>>, <<
==, !=
[]
count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0
set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反
__EOF__

本文作者:ReSakura
本文链接:https://www.cnblogs.com/ReSakura/p/16393342.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/ReSakura/p/16393342.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示