板子
起手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h> using namespace std;#define ios \ std::cin.sync_with_stdio(false); \ std::cin.tie(0); typedef long long ll;#define int long long #define pii pair<int, int> const int N = 500005 ;#define endl '\n' #define Endl '\n' #define ENDL '\n' void slove () {} signed main () { int t; cin >> t; while (t--) { slove (); } }
高精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 const int N = 1e5 ; string a, b; int la, lb, lc; int A[N] = {0 }, B[N] = {0 }, C[N] = {0 };void add (int A[], int B[], int C[]) { for (int i = 0 ; i < lc; i++) { C[i] += A[i] + B[i]; C[i + 1 ] += (C[i] / 10 ); C[i] %= 10 ; } } bool cmp (int A[], int B[]) { if (la != lb) return la > lb; else for (int i = la - 1 ; ~i; i--) if (A[i] != B[i]) return A[i] > B[i]; return 1 ; } void sub (int A[], int B[], int C[]) { if (!cmp (A, B)) cout << "-" , swap (A, B); for (int i = 0 ; i < lc; i++) { if (A[i] < B[i]) A[i] += 10 , A[i + 1 ]--; C[i] = A[i] - B[i]; } while (lc && C[lc] == 0 ) lc--; lc++; } void mul (int A[], int B[], int C[]) { for (int i = 0 ; i < la; i++) for (int j = 0 ; j < lb; j++) { C[i + j] += A[i] * B[j]; C[i + j + 1 ] += C[i + j] / 10 ; C[i + j] %= 10 ; } while (lc && C[lc] == 0 ) lc--; lc++; } void div (int A[], int b, int C[]) { long long temp = 0 ; for (int i = la - 1 ; ~i; i--) { temp = temp * 10 + A[i]; C[i] = temp / b; temp %= b; } while (lc && C[lc] == 0 ) lc--; lc++; } void Input (int A[]) { for (int i = 0 ; i < la; i++) A[la - i - 1 ] = a[i] - '0' ; } void Output (int C[]) { for (int i = lc - 1 ; ~i; i--) cout << C[i]; }
快速幂
快速幂 快速幂取模
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 long long quickpow (long long a, long long n, long long p) { int res = 1 ; while (n) { if (n & 1 ) res = res * a; a = a * a, n >>= 1 ; } return res; } long long quickpowmodp (long long a, long long n, long long p) { int res = 1 ; while (n) { if (n & 1 ) res = res * a % p; a = a * a % p, n >>= 1 ; } return res; }
矩阵快速幂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 long long n, k;const int N = 500 ;const int mod = 1000000007 ;struct Matrix { long long c[N][N] = {0 }; } a, res; Matrix operator *(Matrix &x, Matrix &y) { Matrix t; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) for (int k = 1 ; k <= n; k++) t.c[i][j] = (t.c[i][j] + x.c[i][k] * y.c[k][j]) % mod; return t; } void quickpow (long long k) { for (int i = 1 ; i <= n; i++) res.c[i][i] = 1 ; while (k) { if (k & 1 ) res = res * a; a = a * a, k >>= 1 ; } }
数论
判断素数-试除法
1 2 3 4 5 6 7 8 9 10 11 bool prime (int x) { if (x == 1 ) return 0 ; for (int i = 2 ; i <= x / i; i++) { if (x % i == 0 ) return 0 ; } return 1 ; }
分解质因数
$$
n=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}\dots p_s^{\alpha_s},p_1<p_2<p
_3<\dots<p_S
$$
1 2 3 4 5 6 7 8 9 10 11 12 const int N = 500005 ;int a[N]; void decompose (int x) { for (int i = 2 ; i <= x / i; i++) { while (x % i == 0 ) a[i]++, x /= i; } if (x > 1 ) a[x]++; }
埃氏筛法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const int N = 100000010 ; int vis[N] = {0 }; int prim[N] = {0 }; int cnt; void Eratosthenes (int n) { for (long long i = 2 ; i <= n; ++i) if (!vis[i]) { prim[++cnt] = i; for (long long j = i * i; j <= n; j += i) vis[j] = 1 ; } }
线性筛
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const int N = 100000010 ; int vis[N] = {0 }; int prim[N] = {0 }; int cnt = 0 ; void get_prim (int n) { for (int i = 2 ; i <= n; i++) { if (vis[i] == 0 ) prim[++cnt] = i; for (int j = 1 ; 1ll * i * prim[j] <= n; j++) { vis[i * prim[j]] = 1 ; if (i % prim[j] == 0 ) break ; } } }
欧拉函数
**欧拉函数 **$\varphi(n)$ 指小于 $n$ ,并且与 $n$ 互质的数的个数。
当 $n$ 为质数,$\varphi (n)=p-1$
当 $p$ 为质数,$\varphi (p^k)=(p-1)*p^{k-1}$
当 $n$ 为合数,$\varphi (n)=s*\prod \limits_{i=1}^s{\frac{p_i-1}{pi}}$
$n=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}\dots p_s^{\alpha_s},p_1<p_2<p
_3<\dots<p_S$
欧拉定理
若 $gcd(a,m)=1$ ,则 $a^{\varphi(m)=1(%m)}$;
扩展欧拉定理
$a^b=\begin{align*}\begin{split} \left {\begin{array}{lr}a^b,& b<\varphi(m)\a^{b%\varphi(m)+\varphi(m)},& b\ge\varphi(m)\end{array}\right.\end{split}\end{align*} (%m)$
约数
约数个数定理 :若 $n=\prod \limits_{i=1}^s{p_i^{\alpha_i}}$ ,约数个数 $d(n)=\prod \limits_{i=1}^s{(\alpha_i+1)}$
约数和定理 :若 $n=\prod \limits_{i=1}^s{p_i^{\alpha_i}}$ ,约数和 $f(n)=\prod \limits_{i=1}^s{\sum_{j=0}^{\alpha_{i}}{p_i^j}}$
莫比乌斯函数
莫比乌斯函数 :$\begin{align*}\begin{split}M_{1}= \left {\begin{array}{lr}1,& n=1\0,& n含相同质因子\(-1)^s,& s为n的不同质因子的个数\end{array}\right.\end{split}\end{align*}$
欧几里得算法
$$
当a>b,则gcd(a,b)=gcd(b,a%b)
$$
1 2 3 4 5 6 7 8 9 int gcd (int a, int b) { if (a < b) swap (a, b); if (b == 0 ) return a; else return gcd (b, a % b); }
裴蜀定理
一定存在整数 $x,y$ ,满足 $ax+by=gcd(a,b)$
扩展欧几里得算法
$Q$ :求 $ax+by=gcd(a,b)$ 一组整数解
当 $b=0$ 时,$ax+by=a$,$x=1$,$y=0$
当 $b\neq 0$时,由裴蜀定理 , $ax+by=gcd(a,b)$
$gcd(b,a%b)=bx_1+(a%b)y_1=ay_1+b(x_1-\lfloor\frac{a}{b}\rfloor y_1)$
$x=y_1,y=x_1-\lfloor\frac{a}{b}\rfloor y_1$
$\begin{align*}\begin{split} \left {\begin{array}{lr}x=x_0+\frac{b}{gcd(a,b)}*k\y=y_0-\frac{a}{gcd(a,b)}k\end{array}\right.\end{split}\end{align } (考虑ax+by=0构造)$
1 2 3 4 5 6 7 8 9 10 11 12 int exgcd (int a, int b, int &x, int &y) { if (b == 0 ) { x = 1 , y = 0 ; return a; } int x1, y1, d; d = exgcd (b, a % b, x1, y1); x = y1, y = x1 - a / b * y1; return d; }
组合数
组合数原理
组合数原理 :$C_{a}^{b}=\frac{a!}{b!(a-b)!}=C_{a-1}^{b-1}+C_{a-1}^{b}$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 typedef long long ll;const int N = 100010 , MOD = 1e9 + 7 ;ll f[N] = {0 }; ll g[N] = {0 }; ll qpow (ll a, int b) { ll res = 1 ; while (b) { if (b & 1 ) res = res * a % MOD; a = a * a % MOD; b >>= 1 ; } return res; } void init () { f[0 ] = g[0 ] = 1 ; for (int i = 1 ; i < N; i++) { f[i] = f[i - 1 ] * i % MOD; g[i] = g[i - 1 ] * qpow (i, MOD - 2 ) % MOD; } } ll getC (ll n, ll m) { return f[n] * g[m] % MOD * g[n - m] % MOD; }
卢卡斯定理
卢卡斯定理 :$C_n^m=C_{n/p}^{m/p}C_{n%p}^{m%p}(%p),p为质数$
$C_{p}^{x}=0(%p),0<x<p$
$(1+p)^x=1+x^p(%p)$
1 2 3 4 5 6 7 int lucas (ll n, ll m, int p) { if (m == 0 ) return 1 ; return lucas (n / p, m / p, p) * getC (n % p, m % p, p) % p; }
卡特兰数
卡特兰数 :$C_{2n}^n-C_{2n}^{n-1}=\frac{C_{2n}^n}{n+1}$
二项式反演
二项式定理 :$(a+b)^n=\sum_{i=0}^{n}{C_{n}^{i}a^{n-1}b^i}$;
$\sum_{i=0}^{n}{C_i^n(-1)^i}=[n=0] $
背包
01(逆序)/完全(顺序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int v[N] = {0 }, w[N] = {0 };#define endl '\n' int f[N] = {0 };signed main () { int n, m; cin >> n >> m; for (int i = 1 ; i <= n; i++) cin >> v[i] >> w[i]; for (int i = 1 ; i <= n; i++) f[j] = max (f[j], f[j - v[i]] + w[i]); cout << f[m]; }
多重背包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const int N = 2005 ; int n, m;int v1, w1, s; int v[N * 12 ], w[N * 12 ]; int f[N];int main () { cin >> n >> m; int num = 1 ; for (int i = 1 ; i <= n; i++) { cin >> v1 >> w1 >> s; for (int j = 1 ; j <= s; j <<= 1 ) { v[num] = j * v1; w[num++] = j * w1; s -= j; } if (s) v[num] = s * v1, w[num++] = s * w1; } for (int i = 1 ; i < num; i++) for (int j = m; j >= v[i]; j--) f[j] = max (f[j], f[j - v[i]] + w[i]); cout << f[m]; }
混合背包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int n, m;int f[1010 ], g[1010 ];int q[1010 ];void ZeroOnePack (int v, int w) { for (int j = m; j >= v; j--) f[j] = max (f[j], f[j - v] + w); } void CompletePack (int v, int w) { for (int j = v; j <= m; j++) f[j] = max (f[j], f[j - v] + w); } void MultiplePack (int v, int w, int s) { memcpy (g, f, sizeof (f)); for (int j = 0 ; j < v; j++) { int h = 0 , t = -1 ; for (int k = j; k <= m; k += v) { if (h <= t && q[h] < k - s * v) h++; if (h <= t) f[k] = max (g[k], g[q[h]] + (k - q[h]) / v * w); while (h <= t && g[k] >= g[q[t]] + (k - q[t]) / v * w) t--; q[++t] = k; } } } int main () { int v, w, s; cin >> n >> m; for (int i = 1 ; i <= n; i++) { scanf ("%d %d %d" , &v, &w, &s); if (s == -1 ) ZeroOnePack (v, w); else if (s == 0 ) CompletePack (v, w); else MultiplePack (v, w, s); } cout << f[m]; }
博弈论
Nim游戏
结论 :若初态为必败态 ($a_1\oplus a_2 \oplus a_3 \dots \oplus a_n \neq 0)$ ,先手必败;
若初态为必胜态 ($a_1\oplus a_2 \oplus a_3 \dots \oplus a_n = 0)$ ,先手必胜;
台阶Nim
结论 :只看奇数台阶,转化为Nim游戏 ;
SG函数
有向图游戏 :给定一个有向无环图,图中只有一个起点,在起点上放一个棋子,两个玩家轮流沿着有向边推动棋子,每次走一步,不能走的玩家失败。
$mex(S)$ :不属于集合 $S$ 中的最小非负数;
$SG$函数 :设状态(节点) $x$ 有 $k$ 个后继状态(子节点)$y_1,y_2,\dots,y_k$,$SG(x)=mex({SG(y_1),SG(y_2),\dots,SG(y_k)})$
$SG$定理 :由 $n$ 个有向图游戏组成的组合游戏,设起点分别为 $s_1,s_2,…s_n$ ,当 $SG(s_1) \oplus SG(s_2) \oplus \dots \oplus SG(s_n) \neq 0$,先手必败;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const int N = 2005 , M = 10005 ;int n, m, k, a, b, x;int h[N], to[M], ne[M], tot; int f[N];void add (int a, int b) { to[++tot] = b, ne[tot] = h[a], h[a] = tot; } int sg (int u) { if (f[u] != -1 ) return f[u]; set<int > S; for (int i = h[u]; i; i = ne[i]) S.insert (sg (to[i])); for (int i = 0 ;; i++) if (!S.count (i)) return f[u] = i; } int main () { scanf ("%d%d%d" , &n, &m, &k); for (int i = 0 ; i < m; i++) scanf ("%d%d" , &a, &b), add (a, b); memset (f, -1 , sizeof f); int res = 0 ; for (int i = 0 ; i < k; i++) scanf ("%d" , &x), res ^= sg (x); if (res) puts ("win" ); else puts ("lose" ); }
二分查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int n, a[1000005 ]; int find (int q) { int l = 0 , r = n + 1 ; while (l + 1 < r) { int mid = l + r >> 1 ; if (a[mid] >= q) r = mid; else l = mid; } return a[r] == q ? r : -1 ; }
树
BFS(宽搜)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const int N = 100010 ;vector<int > e[N]; int vis[N];queue<int > q; void bfs (int s) { vis[s] = 1 ; q.push (s); while (q.size ()) { int x = q.front (); q.pop (); for (auto y : e[x]) { if (vis[y]) continue ; vis[y] = 1 ; q.push (y); } } }
DFS(深搜)
1 2 3 4 5 6 7 8 9 10 11 const int N = 100010 ;vector<int > e[N]; void dfs (int u, int fa) { for (auto v : e[u]) { if (v == fa) continue ; dfs (v, u); } }
最小生成树(prim算法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #define inf __INT32_MAX__ const int N = 5010 ;int n, cnt, ans;struct edge { int v, w; }; vector<edge> e[N]; int d[N], vis[N];bool prim (int s) { for (int i = 0 ; i <= n; i++) d[i] = inf; d[s] = 0 ; for (int i = 1 ; i <= n; i++) { int u = 0 ; for (int j = 1 ; j <= n; j++) if (!vis[j] && d[j] < d[u]) u = j; vis[u] = 1 ; ans += d[u]; if (d[u] != inf) cnt++; for (auto ed : e[u]) { int v = ed.v, w = ed.w; if (d[v] > w) d[v] = w; } } return cnt == n; }
最短路 (Dijkstra 算法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #define inf 2147483647 const int N = 100010 ;int n;struct edge { int v, w; }; vector<edge> e[N]; int d[N], vis[N];priority_queue<pair<int , int >> q; void dijkstra (int s) { for (int i = 0 ; i <= n; i++) d[i] = inf; d[s] = 0 ; q.push ({0 , s}); while (q.size ()) { auto t = q.top (); q.pop (); int u = t.second; if (vis[u]) continue ; vis[u] = 1 ; for (auto ed : e[u]) { int v = ed.v, w = ed.w; if (d[v] > d[u] + w) { d[v] = d[u] + w; q.push ({-d[v], v}); } } } }
线段树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 #include <iostream> #include <algorithm> using namespace std;#define lc p << 1 #define rc p << 1 | 1 #define N 100005 #define ll long long ll n, w[N]; struct node { ll l, r, sum, add; } tr[N * 4 ]; void pushup (ll p) { tr[p].sum = tr[lc].sum + tr[rc].sum; } void pushdown (ll p) { auto &u = tr[p], &l = tr[lc], &r = tr[rc]; if (u.add) { l.sum += u.add * (l.r - l.l + 1 ), r.sum += u.add * (r.r - r.l + 1 ), l.add += u.add, r.add += u.add, u.add = 0 ; } } void build (ll p, ll l, ll r) { tr[p] = {l, r, w[l], 0 }; if (l == r) return ; ll m = l + r >> 1 ; build (lc, l, m); build (rc, m + 1 , r); pushup (p); } void update (ll p, ll x, ll y, ll k) { if (x <= tr[p].l && tr[p].r <= y) { tr[p].sum += (tr[p].r - tr[p].l + 1 ) * k; tr[p].add += k; return ; } ll m = tr[p].l + tr[p].r >> 1 ; pushdown (p); if (x <= m) update (lc, x, y, k); if (y > m) update (rc, x, y, k); pushup (p); } ll query (ll p, ll x, ll y) { if (x <= tr[p].l && tr[p].r <= y) return tr[p].sum; ll m = tr[p].l + tr[p].r >> 1 ; pushdown (p); ll sum = 0 ; if (x <= m) sum += query (lc, x, y); if (y > m) sum += query (rc, x, y); return sum; } int main () { ll m, op, x, y, k; cin >> n >> m; for (ll i = 1 ; i <= n; i++) cin >> w[i]; build (1 , 1 , n); while (m--) { cin >> op >> x >> y; if (op == 2 ) cout << query (1 , x, y) << endl; else cin >> k, update (1 , x, y, k); } return 0 ; }
并查集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const int N = 100010 ;int n, m, x, y, z;int fa[N];int find (int x) { if (fa[x] == x) return x; return fa[x] = find (fa[x]); } vector<int > siz (N, 1 ) ; void unionset (int x, int y) { x = find (x), y = find (y); if (x == y) return ; if (siz[x] > siz[y]) swap (x, y); fa[x] = y; siz[y] += siz[x]; }
字符串哈希
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> using namespace std;typedef unsigned long long ull;const int N = 1000010 , MOD = 131 ;char s[N];int n;ull p[N], h[N]; void init () { p[0 ] = 1 , h[0 ] = 0 ; for (int i = 1 ; i <= n; i++) { p[i] = p[i - 1 ] * MOD; h[i] = h[i - 1 ] * MOD + s[i]; } } ull get (int l, int r) { return h[r] - h[l - 1 ] * p[r - l + 1 ]; } bool substr (int l1, int r1, int l2, int r2) { return get (l1, r1) == get (l2, r2); }
命名空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 i64 fpow (i64 x, i64 r) { i64 result = 1 ; while (r) { if (r & 1 )result = result * x % mod; r >>= 1 ; x = x * x % mod; } return result; } namespace binom { i64 fac[N], ifac[N]; int __ = [] { fac[0 ] = 1 ; for (int i = 1 ; i <= N - 5 ; i++) fac[i] = fac[i - 1 ] * i % mod; ifac[N - 5 ] = fpow (fac[N - 5 ], mod - 2 ); for (int i = N - 5 ; i; i--) ifac[i - 1 ] = ifac[i] * i % mod; return 0 ; }(); inline i64 C (int n, int m) { if (n < m || m < 0 )return 0 ; return fac[n] * ifac[m] % mod * ifac[n - m] % mod; } inline i64 A (int n, int m) { if (n < m || m < 0 )return 0 ; return fac[n] * ifac[n - m] % mod; } } using namespace binom;
板子jiangly
并查集(DSU)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 struct DSU { std::vector<int > f, siz; DSU () {} DSU (int n) { init (n); } void init (int n) { f.resize (n); std::iota (f.begin (), f.end (), 0 ); siz.assign (n, 1 ); } int find (int x) { while (x != f[x]) { x = f[x] = f[f[x]]; } return x; } bool same (int x, int y) { return find (x) == find (y); } bool merge (int x, int y) { x = find (x); y = find (y); if (x == y) { return false ; } siz[x] += siz[y]; f[y] = x; return true ; } int size (int x) { return siz[find (x)]; } };