CSP-S考前整理合集

Posted hydrogen-helium

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSP-S考前整理合集相关的知识,希望对你有一定的参考价值。

复赛前整理整理……涨涨RP

本文主要收集整理各类板子的代码。


一·IO优化

快速读入

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

输出

输出不再写单独函数

答案输出用printf,调试信息一律std::cout<<……

二·数论

欧几里得算法

用处:求最大公约数

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

int main() {
    int a, b;
    read(a), read(b);
    int G = gcd(a, b);
}

时间复杂度:(O(logn))

扩展欧几里得算法

用处:求解逆元,解线性同余方程……

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

int main() {
    int a, b, x, y;
    exgcd(a, p, x, y);
    int G = (x % p + p) % p; // a 在 %p意义下的逆元
}

时间复杂度:(O(logn))

快速幂

用处:求解逆元……

#define LL long long
const int MOD = 1e9 + 7;

LL quick_pow(LL a, LL b, LL mod) {
    LL ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans % mod;
}

int main() {
    LL a, b;
    LL G = quick_pow(a, b, MOD); // a ^ b % MOD 
}

时间复杂度:(O(logn))

龟速乘

用处:乘法爆(long long)时使用

#define LL long long
const int MOD = 1e9 + 7;

LL quick_mul(LL a, LL b, LL mod) {
    LL ans = 1;
    while (b) {
        if (b & 1) ans = (ans + a) % mod;
        b >>= 1;
        a = (a + a) % mod;
    }
    return ans % mod;
}

int main() {
    LL a, b;
    LL G = quick_mul(a, b, MOD); // a * b % MOD 
}

时间复杂度:(O(logn))

逆元

用处:计算(frac{a}{b}mod P)

#define LL long long
const int maxn = 3e5 + 5;
const int MOD = 1e9 + 7;
int inv[maxn];

LL quick_pow(LL a, LL b, LL mod) {
    LL ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans % mod;
}

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


int main() {
    LL a;
    LL G = quick_pow(a, MOD - 2, MOD); // a 在模MOD意义下逆元, a必须与MOD互质

    LL x, y;
    exgcd(a, MOD, x, y);
    LL G = (x % MOD + MOD) % MOD // a 在模MOD意义下逆元,无特殊要求

    LL n;
    inv[1] = 1;
    for (int i = 2; i <= n; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD; 
    // 线性求逆元 
}

时间复杂度:前两种均为(O(logn)),最后一种为(O(n))

质因数分解

用处:求一个数的质因数,欧拉函数值……

const int maxn = 3e5 + 5;
int p[maxn], c[maxn], cnt;
void divede(int n) {
    for (int i = 2; i * i <= n; i++) 
        if (n % i == 0) {
            p[++cnt] = i;
            while (n % i == 0) c[i]++, n /= i;
        }
    if (n > 1) p[++cnt] = n, c[n] = 1;
}

int main() {
    int n;
    divide(n);
}

时间复杂度:(O(sqrt n))

线性筛法求素数及积性函数

线性筛素数

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], cnt;

void euler_phi(int n) {
    is_prime[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!is_prime[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            is_prime[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

int main() {
    euler_phi(maxn);
}

线性筛欧拉函数

const int maxn = 3e5 + 5;
int prime[maxn], phi[maxn], cnt;

void phi_table(int n) {
    phi[1] = phi[0] = 0;
    for (int i = 2; i <= n; i++) {
        if (!phi[i]) 
            prime[++cnt] = i, phi[i] = i - 1;
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}

int main() {
    phi_table(maxn);
}

线性筛约数个数

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], d[maxn], st[maxn], cnt;

void phi_table(int n) {
    is_prime[1] = 1, d[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!is_prime[i]) 
            prime[++cnt] = i, d[i] = 2, st[i] = 1;
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            is_prime[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                d[i * prime[j]] = d[i] / (st[i] + 1) * (st[i] + 2);
                st[i * prime[j]] = st[i] + 1; break;
            }
            d[i * prime[j]] = d[i] * d[prime[j]], st[i * prime[j]] = 1;
        }
    }
}

int main() {
    phi_table(maxn);
}

线性筛约数和

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], sd[maxn], st[maxn], cnt;

void phi_table(int n) {
    is_prime[1] = 1, sd[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!is_prime[i]) 
            prime[++cnt] = i, sd[i] = i + 1, st[i] = i + 1;
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            is_prime[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                sd[i * prime[j]] = sd[i] / st[i] + 1 * (st[i] * prime[j] + 1);
                st[i * prime[j]] = st[i] * prime[j] + 1; break;
            }
            sd[i * prime[j]] = sd[i] * sd[prime[j]], st[i * prime[j]] = prime[j] + 1;
        }
    }
}

int main() {
    phi_table(maxn);
}

时间复杂度:以上代码复杂度均为(O(n))

中国剩余定理

用处:求解模数互质的同余方程组

#define LL long long
const int maxn = 3e5 + 5;
LL a[maxn], m[maxn];


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

LL China(int n, LL *a, LL*m) {
    LL G = 1, ans = 0, x, y;
    for (int i = 0; i < n; i++) G *= m[i];
    for (int i = 0; i < n; i++) {
        LL w = G / m[i];
        exgcd(w, m[i], x, y);
        x = (x % m[i] + m[i]) % m[i];
        ans = (ans + x * w * a[i]) % G;
    }
    return (ans + G) % G;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%lld%lld", &a[i], &m[i]);
    printf("%lld
", China(n, a, m));
}

时间复杂度:(O(nlogn))

整除分块

用处:求解类似于(sum_{i=1}^{n}f(i)lfloorfrac{n}{i} floor)的式子,其中(f(i))应该可以用前缀和预处理

#define LL long long

int main() {
    for (LL l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans = (r - l + 1) * (n / l);
    }
    printf("%lld
", ans);
}

时间复杂度:(O(sqrt n))

组合数

int quick_pow(int a, int b, int mod) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans % mod;
}

void init() {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
    for (int i = 1; i <= n; i++) ifac[i] = quick_pow(fac[i], MOD - 2, MOD);
}

int C(int n, int m) {
    return fac[n] % MOD * ifac[m] % MOD * ifac[n - m] % MOD;
}

三·图论

图的存储与遍历

链式前向星

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn];

struct Edge {
    int to, val, nxt;
    Edge(int _to, int _val, int _nxt) {
        this -> to = _to;
        this -> val = _val;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

void dfs(int u, int f) {
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == f) continue;
        dfs(v, u);
    }
}

时间复杂度:(O(n))

vector

using std::pair;
using std::make_pair;
using std::vector;
#define pii pair<int, int>
const int maxn = 3e5 + 5;

vector<pii >G[maxn];

void add(int from, int to, int val) {
    G[from].push_back(make_pair(to, val));
    G[to].push_back(make_pair(from, val));
}

void dfs(int u, int f) {
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == f) continue;
        dfs(v, u);
    }
}

时间复杂度:(O(n)),常数略大……

最短路算法

Floyd多源最短路

int g[1007][1007];

void init() {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i == j) g[i][j] = 0;
            else g[i][j] = 0x3f3f3f3f;
    for (int i = 1; i <= m; i++) {
        int x, y, z;
        read(x), read(y), read(z);
        g[x][y] = g[y][x] = min(g[x][y], z);        
    }
}

void Floyd() {
    for (int k = 1; k <= n; k++) 
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}

时间复杂度:(O(n^3))

Dijkstra单源最短路

using std::priority_queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];

struct Edge {
    int to, val, nxt;
    Edge(int _to, int _val, int _nxt) {
        this -> to = _to;
        this -> val = _val;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

struct Node {
    int w, now;
    bool operator < (const Node &rhs) const {
        return w > rhs.w;
    }
};

void djikstra(int S) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(0));
    priority_queue<Node>pq;
    pq.push((Node){0, S}), dis[S] = 0;
    while (!pq.empty()) {
        Node x = pq.top(); pq.pop();
        int u = x.now();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (dis[v] > dis[u] + edge[i].val) {
                dis[v] = dis[u] + edge[i].val;
                pq.push((Node){dis[v], v});
            }
        }
    }
}

Spfa单源最短路

using std::queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];

struct Edge {
    int to, val, nxt;
    Edge(int _to, int _val, int _nxt) {
        this -> to = _to;
        this -> val = _val;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

void spfa(int S) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    queue<int>q; 
    q.push(S), dis[S] = 0, vis[S] = 1;
    while (!q.empty()) {
        int u = q.front();
        q.pop(); vis[u] = 0;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (dis[v] > dis[u] + edge[i].val) {
                dis[v] = dis[u] + edge[i].val;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    } 
}

并查集

严格讲貌似不属于图论,不过就在这里放着吧……

int fa[maxn];

struct DSU {
    int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    bool judge(int x, int y) {return find(x) == find(y);}
    void merge(int x. int y) {fa[find(x)] = find(y);}
}T;

void init() {
    for (int i = 1; i <= n; i++) fa[i] = i;
    ......
}

时间复杂度:(O(nlogn))

Kruskal最小生成树算法

用处:求最小生成树

struct Edge {
    int from, to, val;
    bool operator < (const Edge &rhs) const {
        return val < rhs.val;
    }
}edge[maxn];

int fa[maxn];

struct DSU {
    int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    bool judge(int x, int y) {return find(x) == find(y);}
    void merge(int x. int y) {fa[find(x)] = find(y);}
}T;

int main() {
    read(n), read(m);
    for (int i = 1; i <= m; i++) read(edge[i].from), read(edge[i].to), read(edge[i].val);
        std::sort(edge + 1, edge + m + 1);
    for (int i = 1; i <= n; i++) fa[i] = i;
    int sum = 0, cnt = 0;
    for (int i = 1; i <= m; i++) {
        int x = edge[i].from, y = edge[i].to;
        if (T.judge(x, y)) continue;
        T.merge(x, y);
        sum += edge[i].val;
        ++cnt;
        if (cnt == n - 1) break;
    }
    printf("%d
", sum);
}

最近公共祖先

倍增LCA

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], fa[maxn][22], lg[maxn];

struct Edge {
    int to, nxt;
    Edge (int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void dfs(int u, int f) {
    dep[u] = dep[f] + 1;
    fa[u][0] = f;
    for (int i = 1; (1 << i) <= dep[u]; i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == f) continue;
        dfs(v, u);
    }
}

int query(int x, int y) {
    if (dep[x] < dep[y]) std::swap(x, y);
    while (dep[x] > dep[y])
        x = fa[x][lg[dep[x] - dep[y]] - 1];
    if (x == y) return x;
    for (int j = lg[dep[x]] - 1; j >= 0; j--)
        if (fa[x][j] != fa[y][j])
            x = fa[x][j], y = fa[y][j];
    return fa[x][0];
}

int main() {
    for (int i = 1; i <= n; i++) lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
    ......
}

时间复杂度:(O(nlogn))

树剖LCA

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], siz[maxn], fa[maxn], son[maxn], top[maxn];

struct Edge {
    int to, nxt;
    Edge (int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void dfs1(int u, int f, int deep) {
    dep[u] = deep, fa[u] = f, siz[u] = 1;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == f) continue;
        dfs1(v, u, deep + 1);
        siz[u] += siz[v];
        if (siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int topf) {
    top[u] = topf;
    if (!son[u]) return ;
    dfs2(son[u], topf);
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

int query(int x, int y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}

int main() {
    ......
    dfs1(1, 0, 1);
    dfs2(1, 1);
    ......
}

Tarjan 算法

割点/割顶

const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], cnt[maxn];

struct Edge {
    int to, nxt;
    Edge (int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int f) {
    dfn[u] = low[u] = ++num;
    int flag = 0;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (!dfn[v]) {
            tarjan(v, fa);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u] && x != fa) cnt[u] = 1;
            if (x == fa) flag++;
        }
        low[u] = min(low[u], dfn[v]);
    }
    if (x == fa && flag >= 2) cnt[u] = 1;
}

割边

const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], bridge[maxn];

struct Edge {
    int to, nxt;
    Edge (int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int in_edge) {
    dfn[u] = low[u] = ++num;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (!dfn[v]) {
            tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) bridge[i] = bridge[i ^ 1] = 1;
        }
        else if (i != (in_edge ^ 1))
            low[u] = min(low[u], dfn[v]);
    }
}

int main() {
    tot = 1;
    ......
}

强连通分量

const int maxn = 3e5 + 5;
int n, m, tot, num, num_node, head[maxn], dfn[maxn], low[maxn], st[maxn], belong[maxn], cnt, vis[maxn];

struct Edge {
    int to, nxt;
    Edge (int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int in_edge) {
    dfn[u] = low[u] = ++num;
    st[++cnt] = u; vis[u] = 1;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (vis[v])
            low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        int y;
        ++num_node;
        while (y = st[cnt--]) {
            belong[y] = num_node;
            vis[y] = false;
            if (u == y) break; 
        }
    }
}

int main() {
    ......
}

时间复杂度:上述代码均为(O(n))

二分图最大匹配

匈牙利算法

用处:据长者说,(CSP)可能要考……

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

bool vis[1005];
int n, m, e, a[1005][1005], belong[1005];

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
} 

bool judge(int x) {
    for (int i = 1; i <= m; i++)
        if (a[x][i] && !vis[i]) {
            vis[i] = 1;
            if (!belong[i] || judge(belong[i])) {
                belong[i] = x;
                return true;
            }
        }
    return false;
}

int main() {
    read(n), read(m), read(e);
    for (int i = 1; i <= e; i++) {
        int x, y;
        read(x), read(y);
        a[x][y] = 1;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        memset(vis, false, sizeof(vis));
        if (judge(i)) ++ans;
    }
    std::cout << ans << '
';
    return 0;
}

时间复杂度:(O(n^2m))

Dinic实现二分图匹配

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define min(a, b) ((a) < (b) ? (a) : (b))
 
const int maxn = 1e6 +5;
const int INF = 1e9;
int n, m, e, head[maxn], tot = -1, s, t;
int maxflow, dep[maxn];
std::queue<int>q;

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

struct Edge {
    int to, val, nxt;
    Edge(int y, int w, int next) {
        to = y, val = w, nxt = next;
    }
    Edge(){
    }
}edge[maxn << 1];

void add(int from, int to, int val)
{
    edge[++tot] = Edge(to, val, head[from]); head[from] = tot;
}

bool bfs(int s, int t)
{
    memset(dep, 0x7f, sizeof(dep));
    while (!q.empty()) q.pop();
    dep[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].to;
            if (dep[v] > INF && edge[i].val) {
                dep[v] = dep[u] + 1;
                q.push(v);
                if (v == t) return true;
            }
        }
    }
    return false;
}

int dfs(int u, int t, int limit)
{
    if (!limit || u == t) return limit;
    int flow = 0, f;
    for (int i = head[u]; i != -1; i = edge[i].nxt) {
        int v = edge[i].to;
        if (dep[v] == dep[u] + 1 && edge[i].val) {
            f = dfs(v, t, min(limit, edge[i].val));
            if (!f) dep[v] = 0;
            flow += f;
            limit -= f;
            edge[i].val -= f;
            edge[i ^ 1].val += f;
            if (!limit) break;
        }
    }
    return flow;
}

void dinic(int s, int t)
{
    while (bfs(s, t))
        maxflow += dfs(s, t, INF);
}

int main()
{
    memset(head, -1, sizeof(head));
    int x, y;
    read(n), read(m), read(e);
    s = n + m + 2, t = n + m + 3;
    for (int i = 1; i <= e; i++) {
        read(x), read(y);
        if (x > n || y > m) continue;
        add(x, y + n, 1);
        add(y + n, x, 0);
    }
    for (int i = 1; i <= n; i++)
        add(s, i, 1), add(i, s, 0);
    for (int i = 1; i <= m; i++)
        add(i + n, t, 1), add(t, i + n, 0);
    dinic(s, t);
    printf("%d
", maxflow);
    return 0;
}

时间复杂度:(O(nsqrt m))

四·动态规划

最长上升子序列LIS

const int INF = 1e9;
int d[100005];

void solve() {
    d[0] = -INF;
    for (int i = 1; i <= n; i++) d[i] = INF;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int pos = std::lower_boud(d, d + i, a[i]) - d;
        d[pos] = std::min(d[pos], a[i]);
        ans = std::max(ans, pos);
    }
}

时间复杂度:(O(nlogn))

最长公共子序列LCS

const int INF = 1e9;
int a[1001], b[1001], f[1001][1001];

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

时间复杂度:(O(n^2))

背包类问题

01背包

const int INF = 1e9;
int w[1001], v[1001], f[10001];

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

时间复杂度:(O(nV))

完全背包

const int INF = 1e9;
int w[1001], v[1001], f[10001];

void solve() {
    for (int i = 1; i <= n; i++) 
        scanf("%d%d", &w[i], &v[i]);
    for (int i = 1; i <= n; i++)
        for (int j = w[i]; j <= V; j++)
            f[i] = std::max(f[i], f[j - w[i]] + v[i]);
}

二进制优化多重背包

const int INF = 1e9;
int w, v, k, f[10001];

void solve() {
    while (n--) {
        scanf("%d%d%d", &w, &v, &k);
        for (int d = 1; d < k; k -= d; d <<= 1) 
            for (int i = V; i >= d * w; i--)
                f[i] = std::max(f[i], f[i - w * d] + d * v);
        for (int i = V; i >= k * w; k--)
            f[i] = std::max(f[i], f[i - w * k] + k * v);
    }
}

时间复杂度:(O(nVlogK))

二维费用背包

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

int f[1007][1007], a[1007], b[1007], c[1007], n, m, x;

int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m >> x;
    for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i];
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= b[i]; j--)
            for (int v = x; v >= c[i]; v--)
            f[j][v] = std::max(f[j][v], f[j - b[i]][v - c[i]] + a[i]);
    cout << f[m][x] << '
';        
    return 0;
}

时间复杂度:(O(nmx))

树形动规问题

【JSOI2018】潜入行动

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define rint register int
#define LL long long
 
const int maxn = 1e5 + 5;
const int MOD = 1e9 + 7;
int n, k, tot, head[maxn], f[maxn][105][4], siz[maxn], g[105][4];

/*
0 -> 安装且被监听
1 -> 安装且不被监听
2 -> 不安装被监听
3 -> 不安装不被监听
*/

struct Edge {
    int to, nxt;
    Edge(int _to, int _nxt) {
        this -> to = _to;
        this -> nxt = _nxt;
    } Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
} 

void dfs(int u, int fa) {
    f[u][1][1] = 1; f[u][0][3] = 1; siz[u] = 1;
    for (rint i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to; 
        if (v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v]; memcpy(g, f[u], sizeof(g));
        for (rint j = min(siz[u], k); ~j; j--) f[u][j][0] = f[u][j][1] = f[u][j][2] = f[u][j][3] = 0;
        for (rint j = min(siz[u], k); ~j; j--) {
            for (rint p = max(0, j + siz[v] - siz[u]); p <= min(siz[v], j); p++) {
                f[u][j][3] += ((LL)f[v][p][2] * g[j - p][3]) % MOD, f[u][j][3] %= MOD;
                f[u][j][2] += ((LL)(f[v][p][0] % MOD + f[v][p][2] % MOD) % MOD) *
                              ((g[j - p][2] % MOD + g[j - p][3] % MOD) % MOD) % MOD; 
                f[u][j][2] %= MOD;
                f[u][j][1] += ((LL)(f[v][p][2] % MOD + f[v][p][3] % MOD)) % MOD * 
                              g[j - p][1] % MOD; f[u][j][1] %= MOD;
                f[u][j][0] += ((LL)(f[v][p][0] + f[v][p][1] % MOD) + (f[v][p][2] + f[v][p][3]) % MOD)
                              % MOD * ((g[j - p][1] % MOD+ g[j - p][0] % MOD)) % MOD; f[u][j][0] %= MOD;
            }
            f[u][j][0] = (f[u][j][0] - f[u][j][1] + MOD) % MOD; 
            f[u][j][2] = (f[u][j][2] - f[u][j][3] + MOD) % MOD;     
        }
    }
}

int main() {
    int x, y;
    read(n), read(k);
    for (int i = 1; i < n; i++) {
        read(x), read(y);
        add(x, y), add(y, x);
    }
    dfs(1, 0);
    std::cout << f[1][k][0] + f[1][k][2]<< '
';
    return 0;
}

时间复杂度:树形动规一般情况下为(O(n)),树形背包为严格(O(n^2))

数位DP

烦人的数学作业

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

const int maxn = 25;
const int MOD = 1e9 + 7;
int T, L, R;
int val[maxn], f[maxn][maxn];

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
    return x *= w;
}

int dfs(int pos, int limit, int lead_zero, int k, int sum) {
    if (!pos) return sum;
    if (!limit && !lead_zero && f[pos][sum] != -1)
        return f[pos][sum];
    int lim = limit ? val[pos] : 9;
    int ans = 0;
    for (int i = 0; i <= lim; i++) {
        if (lead_zero && !i)
            ans += dfs(pos - 1, limit && (i == lim), 1, k, sum);
        else
            ans += dfs(pos - 1, limit && (i == lim), 0, k, sum + (i == k));
    }
    if (!limit && !lead_zero)
        f[pos][sum] = ans;
    return ans;
}

int solve(int n, int k) {
    memset(f, -1, sizeof(f));
    int len = 0;
    while (n) val[++len] = n % 10, n /= 10;
    return dfs(len, 1, 1, k, 0);
}

signed main() {
    read(T);
    while (T--) {
        int ans = 0;
        read(L), read(R);
        for (int i = 1; i <= 9; i++)
            ans += (((solve(R, i) - solve(L - 1, i) + MOD) % MOD) * i % MOD + MOD) % MOD, ans %= MOD;
        printf("%lld
", ans);
    }
    return 0;
}

时间复杂度:(O()玄学不会证())

状态压缩DP

UVA11008 【Antimatter Ray Clearcutting】

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

using namespace std;

const int maxn = 70000; 
int T, n, m, x[20], y[20], g[20][20]; //x,y储存坐标,g用于储存直线
int f[maxn], N;
// f用于记忆化,N为初始状态。
void init()
{
    memset(f, -1, sizeof(f));
    memset(g, 0, sizeof(g));  //多测不清空,爆零两行泪!!!
    scanf("%d%d", &n,   &m);
    N = (1 << n) - 1;  //搜索初始状态
    for (int i = 0; i < n; i++)
        scanf("%d%d", &x[i], &y[i]);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i == j) continue; 
            for (int k = n - 1; k >= 0; k--) {
                g[i][j] <<= 1; //左移一位,为下一个点腾出位置
                if ((x[j] - x[i]) * (y[k] - y[j]) == (y[j] - y[i]) * (x[k] - x[j]))
                    g[i][j]++; //表示这个点在这条直线上
            }
        }
    }
}

int Count(int k)
{
    int cnt = 0;
    for (int i = 0; i < n; i++)
        if ((1 << i) & k) cnt++;
    return cnt;
}

int dfs(int now)
{
    int cnt = Count(now); 
    // Count用于计算还有多少个点没删
    int& ans = f[now]; 
    // 这个取地址符一定要加!! 原因是后面f[now]也要改变
    if (cnt <= n - m) return ans = 0; 
    // 如果剩下的点小于n-m,那么就不用再删了,直接返回0
    else if (cnt == 1) return ans = 1;
    //如果还剩一个点,直接用一条直线来删
    else if (ans > -1) return ans;
    //记忆化,不解释
    ans = (1 << 30);
    //都没有,枚举直线更新状态
    for (int i = 0; i < n; i++) {
        if ((1 << i) & now) {
            // 保证i这个点还没有被删
            for (int j = i + 1; j < n; j++) {
                if ((1 << j) & now) {
                    //保证j这个点没被删
                    int temp = now & (g[i][j] ^ N); //异或取补集,再取and,得到下一个状态
                    ans = min(ans, dfs(temp) + 1); //更新ans
                }
            }
        }
    }
    return ans;
}

int main()
{
    int t = 1;
    scanf("%d", &T);
    while (T--) {
        init(); //初始化
        printf("Case #%d:
%d
", t++, dfs(N));
        if (T) puts("");
    }
    return 0;
}

时间复杂度:一般为指数级貌似是废话

五·数据结构

线段树

用处:不说了,大家都知道……

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

const int maxn = 1e5 + 5;
int n, m, a[maxn], opt, l, r, v;

struct Node {
    int sum, tag;
}z[maxn << 2];

void build(int rt, int l, int r) {
    if (l == r) {
        z[rt].sum = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
    z[rt].sum = z[rt << 1].sum + z[rt << 1 | 1].sum;
}

void modify(int rt, int l, int r, int x, int y, int v) {
    z[rt].sum += (min(r, y) - max(l, x) + 1) * v;
    if (x <= l && r <= y) {
        z[rt].tag += v;
        return ;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) modify(rt << 1, l, mid, x, y, v);
    if (y > mid) modify(rt << 1 | 1, mid + 1, r, x, y, v);
}

int query(int rt, int l, int r, int x, int y, int tg) {
    if (x <= l && r <= y) {
        return z[rt].sum + (min(r, y) - max(l, x) + 1) * tg;
    }
    int mid = (l + r) >> 1;
    int ret = 0;
    if (x <= mid) ret += query(rt << 1, l, mid, x, y, tg + z[rt].tag);
    if (y > mid) ret += query(rt << 1 | 1, mid + 1, r, x, y, tg + z[rt].tag); 
    return ret;
}

signed main() {
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        scanf("%lld", &opt);
        if (opt == 1) {
            scanf("%lld%lld%lld", &l, &r, &v);
            modify(1, 1, n, l, r, v);
        }
        else {
            scanf("%lld%lld", &l, &r);
            printf("%lld
", query(1, 1, n, l, r, 0));
        }
    }   
    return 0;
}

时间复杂度:(O(nlogn))

树状数组

用处:大家都知道……

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define lowbit(x) ((x) & -(x))

const int maxn = 5e5 + 5;
int n, m;

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

struct FenwickTree {
    int bit[maxn];
    void add(int x, int v) {for (; x <= n; x += lowbit(x)) bit[x] += v;}
    int query(int x) {int ret = 0; for (; x; x -= lowbit(x)) ret += bit[x]; return ret;}
}T;

signed main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        int temp; read(temp);
        T.add(i, temp);
    }
    int opt, x, y;
    for (; m; m--) {
        read(opt), read(x), read(y);
        if (opt == 1) T.add(x, y);
        else printf("%lld
", T.query(y) - T.query(x - 1));
    }
    return 0;
}

时间复杂度:(O(nlogn))

ST表

用处:………………

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

const int maxn = 1e6 + 5;
int n, m, f[maxn][22];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &f[i][0]);
    for (int j = 1; (1 << j) <= n; j++)
        for (int i = n; i - (1 << j) + 1 >= 1; i--)
            f[i][j] = std::max(f[i][j - 1], f[i - (1 << (j - 1))][j - 1]);
    for (; m; m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int j = log2(r - l + 1);
        int ans = std::max(f[r][j], f[l + (1 << j) - 1][j]);
        printf("%d
", ans);
    }
    return 0;
}

时间复杂度:预处理(O(nlogn)),查询(O(1))

主席树

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

const int maxn = 2e5 + 5;
int n, m, a[maxn], b[maxn];

struct Node {int l, r, sum; Node(){l = r = sum = 0;}}z[maxn << 5];
int cnt, root[maxn];

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

void build(int &rt, int l, int r) {
    rt = ++cnt; if (l == r) return ;
    int mid = (l + r) >> 1;
    build(z[rt].l, l, mid), build(z[rt].r, mid + 1, r);
}

void modify(int &rt, int pre, int l, int r, int pos) {
    rt = ++cnt;
    z[rt].l = z[pre].l, z[rt].r = z[pre].r, z[rt].sum = z[pre].sum + 1;
    if (l == r) return ; int mid = (l + r) >> 1;
    if (pos <= mid) modify(z[rt].l, z[pre].l, l, mid, pos);
    else modify(z[rt].r, z[pre].r, mid + 1, r, pos);
}

int query(int L, int R, int l, int r, int pos) {
    if (l == r) return l; int mid = (l + r) >> 1;
    int temp = z[z[R].l].sum - z[z[L].l].sum;
    if (pos <= temp) return query(z[L].l, z[R].l, l, mid, pos);
    else return query(z[L].r, z[R].r, mid + 1, r, pos - temp);
}

int main() {
    read(n), read(m); int x, y, k;
    for (int i = 1; i <= n; i++) b[i] = read(a[i]);
    std::sort(b + 1, b + n + 1);
    int len = std::unique(b + 1, b + n + 1) - b - 1;
    build(root[0], 1, len);
    for (int i = 1; i <= n; i++) {
        int pos = std::lower_bound(b + 1, b + len + 1, a[i]) - b;
        modify(root[i], root[i - 1], 1, len, pos);
    }
    for (int i = 1; i <= m; i++) {
        read(x), read(y), read(k);
        printf("%d
", b[query(root[x - 1], root[y], 1, len, k)]);
    }
    return 0;
}

时间复杂度:(O(nlogn))

FHQ Treap

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define pii pair<int, int>

using namespace std;

const int maxn = 1e5 + 5;
int w, ch;

struct Node {
    int key, val;
    int ls, rs, size;
}g[maxn];
int root, cnt;

template<class T>
T read(T &x)
{
    x = 0, w = 1, ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
    return x *= w;
}

void pushup(int p)
{
    g[p].size = g[g[p].ls].size + g[g[p].rs].size + 1;
}

void insert(int x)
{
    ++cnt;
    g[cnt].val = x;
    g[cnt].size = 1;
    g[cnt].key = rand();
    g[cnt].ls = g[cnt].rs = 0;
}

int merge(int x, int y)
{
    if (x == 0) return y;
    if (y == 0) return x;
    if (g[x].key < g[y].key) {
        g[x].rs = merge(g[x].rs, y);
        pushup(x);
        return x;
    }
    else {
        g[y].ls = merge(x, g[y].ls);
        pushup(y);
        return y;
    }
}

void split(int u, int x, int &l, int &r)
{
    if (!u) {l = r = 0; return;}
    if (g[u].val <= x) {l = u; split(g[u].rs, x ,g[u].rs, r);}
    else {r = u; split(g[u].ls, x, l, g[u].ls);}
    pushup(u);
}

int rnk(int u, int num){
    if (num <= g[g[u].ls].size) {
        int ret = rnk(g[u].ls, num);
        return ret;
    }   
    else{
        if (num == g[g[u].ls].size + 1) return u;
        else {
            num -= g[g[u].ls].size + 1;
            return rnk(g[u].rs, num);
        }
    }
}

int main()
{
    int n, temp, x, l, r;
    read(n);
    for (int i = 1; i <= n; i++) {
        read(temp), read(x);
        switch (temp) {
            case 1:
                split(root, x, l, r);
                insert(x);
                root = merge(merge(l, cnt), r);
                break;
            case 2:
                int temp;
                split(root, x, l, temp);
                split(l, x - 1, l, r);;
                r = merge(g[r].ls, g[r].rs);
                root = merge(merge(l, r), temp);
                break;
            case 3:
                split(root, x - 1, l, r);
                printf("%d
", g[l].size + 1);
                root = merge(l, r);
                break;
            case 4:
                printf("%d
", g[rnk(root, x)].val);
                break;
            case 5:
                split(root, x - 1, l, r);
                printf("%d
", g[rnk(l, g[l].size)].val);
                root = merge(l, r);
                break;
            case 6:
                split(root, x, l, r);
                printf("%d
", g[rnk(r, 1)].val);
                root = merge(l, r);
                break;
        }
    }
    return 0;
}

时间复杂度:(O(nlogn))

六·字符串算法

KMP字符串匹配算法

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

const int maxn = 1e6 + 6;
int kmp[maxn], j;
char a[maxn], b[maxn];

int main() {
    scanf("%s", a + 1); scanf("%s", b + 1);
    int lena = strlen(a + 1), lenb = strlen(b + 1);
    for (int i = 2; i <= lenb; i++) {
        while (j && b[i] != b[j + 1])
            j = kmp[j];
        if (b[i] == b[j + 1]) j++;
        kmp[i] = j;
    }
    j = 0;
    for (int i = 1; i <= lena; i++) {
        while (j && a[i] != b[j + 1])
            j = kmp[j];
        if (a[i] == b[j + 1]) j++;
        if (j == lenb) {printf("%d
", i - lenb + 1); j = kmp[j];}
    }
    for (int i = 1; i <= lenb; i++) printf("%d ", kmp[i]); puts("");
    return 0;
}

时间复杂度:(O(n+m))

Manacher算法

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

using std::string;
const int maxn = 11000002;
string x, s;
int radius[maxn << 1], max_right, mid, ans;

int main() {
    std::cin >> x;
    s += '&', s += '$';
    for (int i = 0; i < x.length(); i++) s += x[i], s += '$';
    for (int i = 1; i < s.length(); i++) {
        radius[i] = max_right > i ? min(radius[mid * 2 - i], max_right - i) : 1;
        while (s[i - radius[i]] == s[i + radius[i]]) radius[i]++;
        if (i + radius[i] > max_right) max_right = i + radius[i], mid = i;
        ans = max(ans, radius[i] - 1);
    }
    printf("%d
", ans);
    return 0;
}

时间复杂度:(O(n))

Trie树

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

const int maxn = 5e5 + 5;
int n, m;

struct Trie {
    int ch[maxn][26], siz;
    bool vis[maxn];

    Trie() {
        siz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
        memset(vis, false, sizeof(vis));
    }

    void insert(char *s) {
        int root = 0, len = strlen(s + 1);
        for (int i = 1; i <= len; i++) {
            int ooo = s[i] - 'a';
            if (!ch[root][ooo]) {
                memset(ch[siz], 0, sizeof(ch[siz]));
                ch[root][ooo] = siz++;
            }
            root = ch[root][ooo];
        }
    }

    int search(char *s) {
        int root = 0, len = strlen(s + 1);
        for (int i = 1; i <= len; i++) {
            int ooo = s[i] - 'a';
            if (!ch[root][ooo]) return 0;
            root = ch[root][ooo];
        }
        if (!vis[root]) {
            vis[root] = true;
            return 1;
        }
        return 2;
    }
}trie;

signed main() {
    scanf("%lld", &n);
    char s[100];
    for (int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        trie.insert(s);
    }
    scanf("%lld", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", s + 1);
        int ankh = trie.search(s);
        if (ankh == 0) puts("WRONG");
        else if (ankh == 1) puts("OK");
        else puts("REPEAT");
    }
    return 0;
}

总时间复杂度:(O(n * len))

七·其它

高精度计算

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

using namespace std;
const int maxn = 2005;

struct BigNum {
    int d[maxn], len;

    void clean() {while (len > 1 && !d[len - 1]) len--;}
    BigNum() {memset(d, 0, sizeof(d)); len = 1;}
    BigNum(int num) {*this = num;}
    BigNum(char *num) {*this = num;}

    BigNum operator = (const char *num) {
        memset(d, 0, sizeof(d)), len = strlen(num);
        for (int i = 0; i < len; i++) d[i] = num[len - i - 1] - '0';
        clean();
        return *this;
    }
    BigNum operator = (int num) {
        char s[20]; sprintf(s, "%d", num);
        *this = s;
        return *this;
    }

    BigNum operator + (const BigNum &b) {
        BigNum c = *this; int i;
        for (i = 0; i < b.len; i++) {
            c.d[i] += b.d[i];
            if (c.d[i] > 9) c.d[i] %= 10, c.d[i + 1]++;
        }
        while (c.d[i] > 9) c.d[i++] %= 10, c.d[i]++;
        c.len = max(len, b.len);
        if (c.d[i] && c.len <= i) c.len = i + 1;
        return c;
    }

    BigNum operator - (const BigNum &b) {
        BigNum c = *this; int i;
        for (i = 0 ; i < b.len; i++) {
            c.d[i] -= b.d[i];
            if (c.d[i] < 0) c.d[i] += 10, c.d[i + 1]--;
        }
        while (c.d[i] < 0) c.d[i++] += 10, c.d[i]--;
        c.clean();
        return c;
    }

    BigNum operator * (const BigNum &b) const {
        BigNum c; int i, j; c.len = (len + b.len);
        for (j = 0 ; j < b.len; j++)
            for (i = 0; i < len; i++)
                c.d[i + j] = d[i] * b.d[j];
        for (i = 0; i < c.len - 1; i++)
            c.d[i + 1] += c.d[i] / 10, c.d[i] %= 10;
        c.clean();
        return c;
    }

    BigNum operator / (const BigNum &b) {
        int i, j;
        BigNum c = *this, last = 0;
        for (i = len - 1; i >= 0; i--) {
            last = last * 10 + d[i];
            for (j = 0 ; j < 10; j++) if (last < b * (j + 1)) break;
            c.d[i] = j;
            last = last - b * j;
        }
        c.clean();
        return c;
    }

    BigNum operator % (const BigNum &b) {
        int i, j;
        BigNum last = 0;
        for (i = len - 1; i >= 0; i--) {
            last = last * 10 + d[i];
            for (j = 0; j < 10; j++) if (last < b * (j + 1)) break;
            last = last - b * j;
        }
        return last;
    }

    bool operator < (const BigNum &b) const {
        if (len != b.len) return len < b.len;
        for (int i = len - 1; i >= 0; i--)
            if (d[i] != b.d[i]) return d[i] < b.d[i];
        return false;
    }

    string str() const {
        char s[maxn] = {};
        for (int i = 0; i < len; i++) s[len - i - 1] = d[i] + '0';
        return s;
    }
};

istream& operator >> (istream& in, BigNum &x) {
    string s;
    in >> s;
    x = s.c_str();
    return in;
}

ostream& operator << (ostream& out, const BigNum &x) {
    out << x.str();
    return out;
}

int main() {
    BigNum a, b;
    cin >> a >> b;
    cout << a + b << '
';
    return 0;
}

矩阵快速幂加速递推

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

using namespace std;

const int MOD = 1e9 + 7;
int n;

struct Matrix {
    int a[4][4];
    Matrix(){memset(a, 0, sizeof(a));}
}ans, base;

Matrix operator * (const Matrix &x, const Matrix &y)
{
    Matrix ret;
    for (int i = 1; i <= 2; i++)
        for (int j = 1; j <= 2; j++)
            for (int k = 1; k <= 2; k++)
                ret.a[i][j] = (ret.a[i][j] + x.a[i][k] * y.a[k][j]) % MOD;
    return ret;
}

void init()
{
    base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
    base.a[2][2] = 0;
    ans.a[1][1] = ans.a[1][2] = 1;
}

void quick_pow(int b)
{
    while (b) {
        if (b & 1) ans = ans * base;
        base = base * base;
        b >>= 1;
    }
}

signed main()
{
    init();
    scanf("%lld", &n);
    if (n <= 2) {puts("1"); return 0;}
    quick_pow(n - 2);
    printf("%lld
", ans.a[1][1]);
    return 0;
}

时间复杂度:快速幂(O(logn)),根据矩阵大小不同会带不同大小的常数

大模拟

猪国杀为例

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#define loves =

using std::cin;
using std::cout;
using std::vector;
using std::queue;
using std::string;
int n, m; // n -> 玩家数目 m -> 牌堆牌的数量
int sf[12]; // 身份
int lsf[12]; // 身份确定前的类身份
int health[12]; // 血量
bool crossbow[12]; // 猪哥连弩
bool died[12]; // 是否已死
vector<char>shoupai[12]; // 每只猪手牌
queue<char>heap; //牌堆

// ============card部分============
char get_card() { // 牌堆里抽卡
    char ch = heap.front();
    heap.pop();
    if (heap.empty()) heap.push(ch);
    return ch;
}

void Erase_card(int rt, char ch) {
    for (auto it = shoupai[rt].begin(); it != shoupai[rt].end(); it++)
        if (*it == ch) {
            shoupai[rt].erase(it);
            return ;
        }
}

bool has_card(int rt, char ch) {
    for (auto it : shoupai[rt])
        if (it == ch) return true;
    return false;
}
// ============card部分============

// ============读入部分============
void input() {
    cin >> n >> m;
    for (int i =1 ; i <= n; i++) {
        string temp;
        cin >> temp; health[i] = 4;
        if (temp[0] == 'M' || temp[0] == 'Z') sf[i] = 1;
        else sf[i] = 2;
        for (int j = 1; j <= 4; j++) {
            char ch;
            cin >> ch;
            shoupai[i].push_back(ch);
        }
    }
    while (m--) {
        char ch;
        cin >> ch;
        heap.push(ch);
    }
}
// ============读入部分============

// ============输出部分============
void output() {
    int num_good = 0, num_bad = 0;
    for (int i = 1; i <= n; i++)
        if (!died[i])
            if (sf[i] == 1)
                num_good++;
            else num_bad++;
    if (num_good && !died[1]) puts("MP");
    else puts("FP");
    for (int i = 1; i <= n; i++) {
        if (died[i]) puts("DEAD");
        else {
            for (auto it : shoupai[i]) cout << it << ' ';
            puts("");
        }
    }
    exit(0);
}
// ============输出部分============

// ============无懈可击============
bool can_WX(int fr, int rt, bool isWX) {
    // fr used WX for rt
    int nxt = fr;
    do {
        if (!died[nxt] && (isWX ? sf[nxt] != lsf[rt] : sf[nxt] == lsf[rt]) && has_card(nxt, 'J')) {
            Erase_card(nxt, 'J');
            lsf[nxt] = sf[nxt];
            if (can_WX(nxt, nxt, true))
                return false;
            else return true;
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    } while (nxt != fr);
    return false;
}
// ============无懈可击============

// ============掉血============
void be_heart(int fr, int rt, bool jinnang) {
    health[rt]--;
    //std::cout << rt << "heart by" << fr << '
';
    if (rt == 1 && lsf[fr] == 0)
        lsf[fr] = 3;
    if (!jinnang && lsf[fr] && lsf[fr] != 3)
        lsf[fr] = sf[fr];
    if (health[rt]) return ;
    if (!health[rt] && has_card(rt, 'P')) {
        Erase_card(rt, 'P');
        ++health[rt];
    }
    else {
        died[rt] = true;
        if (rt == 1) output();
        int num_bad = 0;
        for (int i =1 ; i <= n; i++)
            if (!died[i])
                if (sf[i] == 2) num_bad++;
        if (num_bad == 0) output();
        else {
            if (fr == 1 && sf[rt] == 1) {
                shoupai[1].clear();
                crossbow[1] = false;
            }
            if (sf[rt] == 2) {
                shoupai[fr].push_back(get_card());
                shoupai[fr].push_back(get_card());
                shoupai[fr].push_back(get_card());
            }
        }
    }
}
// ============掉血============

// ============南猪入侵============
void use_NM(int rt) {
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        if (!died[nxt]) {
            if (can_WX(rt, nxt, false)) {
                ++nxt;
                if (nxt == n + 1) nxt = 1;
                continue;
            }
            if (has_card(nxt, 'K'))
                Erase_card(nxt, 'K');
            else {
                be_heart(rt, nxt, true);
                //std::cout << rt << "uesd NM hit" << nxt << "heart 1 滴血" << '
';
            }
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
}
// ============南猪入侵============

// ============万箭齐发============
void use_WJ(int rt) {
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        if (!died[nxt]) {
            if (can_WX(rt, nxt, false)) {
                ++nxt;
                if (nxt == n + 1) nxt = 1;
                continue;
            }
            if (has_card(nxt, 'D'))
                Erase_card(nxt, 'D');
            else {
                be_heart(rt, nxt, true);
                //std::cout << rt << "uesd WJ hit" << nxt << "heart 1 滴血" << '
';
            }
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
}
// ============万箭齐发============

// ============决斗============
void duel(int fr, int to) {
    if (!has_card(to, 'K') || (fr == 1 && sf[to] == 1)) {be_heart(fr, to, true); return ;}
    else {
        Erase_card(to, 'K');
        duel(to, fr);
    }
}
// ============决斗============

// ============寻找下一个杀或决斗的目标============
int Find(int rt, bool limit) {
    if (!limit && sf[rt] == 2) return 1;
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        //std::cout << "now find mubiao:" << nxt << '
';
        if (died[nxt]) {
            ++nxt;
            if (nxt == n + 1) nxt = 1;
            continue;
        }
        bool canhit = (rt == 1 ? lsf[nxt] >= 2 : lsf[nxt] != sf[rt]);
        if (rt > 1) {
            if (lsf[nxt] == 0 || lsf[nxt] == 3) canhit = false;
            else canhit = (lsf[nxt] != sf[rt]);
        }
        if (canhit) return nxt;
        else if (limit) return 0;
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
    return 0;
}
// ============寻找下一个杀或决斗的目标============

// ============游戏过程============
void solve() {
    lsf[1] = 1;
     int rt = 0;
     while (true) {
        ++rt;
        if (rt == n + 1) rt = 1;
        if (died[rt]) continue;
        shoupai[rt].push_back(get_card());
        shoupai[rt].push_back(get_card());
        bool usedk = false;
        while (true) {
            if (died[rt]) break;
            bool usedcard = false;
            for (auto i = shoupai[rt].begin(); i != shoupai[rt].end(); i++) {
                char it = *i;
                if (it == 'P') {
                    if (health[rt] != 4) {
                        ++health[rt];
                        Erase_card(rt, 'P');
                        usedcard = true;
                        break;
                    }
                }
                else if (it == 'N'){
                    Erase_card(rt, 'N');
                    use_NM(rt);
                    usedcard = true;
                    break;
                }
                else if (it == 'W'){
                    Erase_card(rt, 'W');
                    use_WJ(rt);
                    usedcard = true;
                    break;
                }
                else if (it == 'Z') {
                    crossbow[rt] = true;
                    Erase_card(rt, 'Z');
                    usedcard = true;
                    break;
                }
                else if (it == 'K') {
                    if (usedk && !crossbow[rt]) continue;
                    int to = Find(rt, true);
                    if (!to) continue;
                    Erase_card(rt, 'K');
                    usedcard = usedk = true;
                    if (has_card(to, 'D')) {Erase_card(to, 'D'); ++health[to];}
                    be_heart(rt, to, false);
                    break;
                }
                else if (it == 'F') {
                    int to = Find(rt, false);
                    if (!to) continue;
                    lsf[rt] = sf[rt];
                    Erase_card(rt, 'F');
                    usedcard = true;
                    if (can_WX(rt, to, false)) break;
                    duel(rt, to);
                    usedcard = true;
                    break;
                }
            }
            if (usedcard) continue;
            break;
        }
     }
}
// ============游戏过程============

int haj() {
    input();
    solve();
    return 0;
}

int ziiidan loves haj();

int main() {}

大概……也许……就这些了吧……

(CSP-S 2019) (RP++!!!)

以上是关于CSP-S考前整理合集的主要内容,如果未能解决你的问题,请参考以下文章

CSP-S2 2019 游记

数据库常考选择题合集

CSP-S 2019 游记

CSP-S 2019 第一轮 游记

csp-s模拟99

CSP-S2019 退役记