(整理)常用模板

 算法模板

快读 & 快写 

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

基础算法

高精度

高精度加法
// 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;
}

龟速乘 

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

二维前缀和

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

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

二分 

返回第一个

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

求严格单调递增子序列长度 

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

DP

背包问题

背包问题的三种情况的处理方式

  1. 体积最多是V,能获得的最大价值 memset(f, 0, sizeof f)
  2. 体积恰好是V,能获得的最少价值 memset(f, 0x3f, sizeof f), f[0] = 0
  3. 体积至少是V 能获得的最少价值, memset(f, 0x3f, sizeof f), f[0] = 0, f[j] = min(f[max(0, j - v[i])], f[j])

最长公共子序列

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

另外两个序列都是全排列的话,就可以找映射关系来求最长上升子序列。

树上求最长路径

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

树形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;
    }
	......
}

枚举子集

for (int i = x; i; i = (i - 1) & x)

数据结构

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

并查集判二分图

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

单调队列

滑动窗口

// 常见模型:找出滑动窗口中的最大值/最小值
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;
}

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

线段树 (二)

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

线段树合并

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

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

树链剖分

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

树状数组

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

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

字符串哈希

核心思想:将字符串看成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];
}

图论

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

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

数学

线性筛

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

线性筛求莫比乌斯函数

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

欧拉函数

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

康托展开

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

组合数

预处理逆元求组合数

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

卡特兰数

给定n个0和n个1,它们按照某种顺序排成长度为2n的序列,满足任意前缀中0的个数都不少于1的个数的序列的数量为: Cat(n) = C(2n, n) / (n + 1)

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

扩展欧几里德求逆元

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

中国剩余定理

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

狄利克雷卷积

定义两个函数\(f\), \(g\) 的狄利克雷卷积为 $ f * g $ , 其自成一个函数。

有:$$ (f*g)(n) = \sum_{d|n}f(d)g(\frac{n}{d}) $$

杜教筛

\[g(1) S(n)=\sum_{i=1}^{n}(f * g)(i)-\sum_{i=2}^{n} g(i) S\left(\left\lfloor\frac{n}{i}\right\rfloor\right) \]

给定一个正整数,求

\[ans_1=\sum_{i=1}^n\varphi(i) \]

\[ans_2=\sum_{i=1}^n \mu(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;
}

判凸包

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

小知识 

cf的保护分

前六场初始分:Promotions of the displayed rating will be equal to 500,350,250,150,100,50 (in total exactly 1400).

数据范围所能使用的时间复杂度 

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

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

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

取模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>;

 臭氧优化 

#pragma GCC optimize(3,"Ofast","inline")

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位取反
posted @ 2022-06-20 15:18  ReSakura  阅读(63)  评论(0)    收藏  举报