记录目前接触过的算法。
 
基础算法+数据结构 二分 1 2 3 4 5 6 7 8 9 10 11 12 int  b_find (int  l, int  r, int  x)     if (l == r){         if (a[l] == x) return  l;         return  -1 ;     }     int  mid = l + r >> 1 ;     if (a[mid] >= x) r = mid;     else  l = mid + 1 ;     return  b_find (l, r, x); } 
查找的是大于等于x的最小数的位置。
离散化 
去重离散化C++
1 2 sort (alls.begin (), alls.end ());alls.erase (unique (alls.begin (), alls.end ()), alls.end ()); 
1 2 3 int  to_find (int  x)      return  lower_bound (alls.begin (), alls.end (), x) - alls.begin () + 1 ; }  
映射离散化
 
ST表 	倍增思想。
	ST 表是用于解决 可重复贡献问题  的数据结构。,预处理时间复杂度O(nlogn),查询O(1)。
	st[i][j]表示第 i 个数开始 $2^j$ 个数中的最值。
1 2 3 4 5 6 7 8 9 10 void  initlog ()      for  (int  i = 2 ; i < N; i++)log_2[i] = log_2[i >> 1 ] + 1 ; } void  initst ()      for  (int  j = 1 ; j < 20 ; j++) {         for  (int  i = 1 ; i + (1  << j) - 1  <= n; i++) {             st[i][j] = max (st[i][j - 1 ], st[i + (1  << j - 1 )][j - 1 ]);         }     } } 
1 2 3 4 int  query (int  l, int  r)     int  x = log_2[r - l + 1 ];     return  max (st[l][x], st[r - (1  << x) + 1 ][x]); } 
树状数组 单点修改 区间查询
1 2 3 4 5 6 7 8 9 10 11 12 13 int  lowbit (int  x)  return  x & -x; }void  add (int  u, int  k)      for  (int  i = u; i <= n; i += lowbit (i))         c[i] += k; } int  query (int  u)      int  res = 0 ;     for  (int  i = u; i > 0 ; i -= lowbit (i))         res += c[i];     return  res; } 
区间修改 单点询问
1 2 3 add (l, x), add (r + 1 , -x);query (x);
线段树(求和模板) 初始准备(注意开四倍的数组)
1 2 const  int  N = 1e5  + 10 ;int  tr[N << 2 ], mark[N << 2 ];
上传与下传
1 2 3 4 5 6 7 8 9 10 11 12 void  push_up (int  u)      tr[u] = tr[u << 1 ] + tr[u << 1  | 1 ]; } void  push_down (int  u, int  len)      if  (len <= 1 ) return ;     tr[u << 1 ] += mark[u] * (len - len / 2 );     mark[u << 1 ] += mark[u];     tr[u << 1  | 1 ] += mark[u] * (len / 2 );     mark[u << 1  | 1 ] += mark[u];     mark[u] = 0 ; } 
建树
1 2 3 4 5 6 7 void  build (int  u = 1 , int  cl = 1 , int  cr = n)      if  (cl == cr) return  void (tr[u] = a[cl]);     int  mid = cl + cr >> 1 ;     build (u << 1 , cl, mid);     build (u << 1  | 1 , mid + 1 , cr);     push_up (u); } 
区间修改
1 2 3 4 5 6 7 8 9 10 11 void  update (int  l, int  r, int  k, int  u = 1 , int  cl = 1 , int  cr = n)      if  (l <= cl && cr <= r) {         tr[u] += k * (cr - cl + 1 );         return  void  (mark[u] += k);     }     push_down (u, cr - cl + 1 );     int  mid = cl + cr >> 1 ;     if  (mid >= l) update (l, r, k, u << 1 , cl, mid);     if  (mid < r) update (l, r, k, u << 1  | 1 , mid + 1 , cr);     push_up (u); } 
区间查询
1 2 3 4 5 6 7 8 int  query (int  l, int  r, int  u = 1 , int  cl = 1 , int  cr = n)      if  (l <= cl && cr <= r) return  tr[u];     push_down (u, cr - cl + 1 );     int  mid = cl + cr >> 1 , ans = 0 ;     if  (mid >= l) ans += query (l, r, u << 1 , cl, mid);     if  (mid < r) ans += query (l, r, u << 1  | 1 , mid + 1 , cr);     return  ans; } 
并查集 	查询操作
1 2 3 int  find (int  x)     return  p[x] == x ? x : p[x] = find (p[x]); } 
	合并操作
 (可同时维护集合元素个数,距离祖宗距离等信息)
图论 最短路 1. Dijkstra(堆优化版) “每次更新距离起点dist最短的点作更新”。 $O(mlogm)$
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 priority_queue<PII, vector<PII>, greater<PII>>q; int  dijkstra (int  s)     dist.assign (N, INF);         memset (vis, 0 , sizeof (vis));     dist[s] = 0 ;          q.emplace (0 , s);     while (!q.empty ()){         int  u = q.top ().second;         q.pop ();                  if (vis[u]) continue ;         vis[u] = 1 ;                  for (auto  &v : g[u]){             if (dist[v.second] > dist[u] + v.first){                 dist[v.second] = dist[u] + v.first;                 q.emplace (dist[v.second], v.second);             }         }     }     if (dist[n] == INF) return  -1 ;     return  dist[n]; } 
2. Bellman-ford 可处理负权边。最适用于,有边数限制的最短路。(时间复杂度较大,少见)  $O(mn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int  bellman_ford (int  s)      memset (dist, 0x3f , sizeof (dist));     dist[s] = 0 ;     for  (int  i = 0 ; i < k; i++) {          memcpy (backup, dist, sizeof (dist));         for  (int  j = 0 ; j < m; j++) {             int  u = edges[j].u, v = edges[j].v, w = edges[j].w;             dist[v] = min (dist[v], backup[u] + w);         }     }     if  (dist[n] > INF / 2 ) return  -INF;     return  dist[n]; } 
3. SPFA 可处理负权边(不含负环)。也可以处理正权边(可能会被卡)
vis记录在队列内的元素。一般$O(m)$ 最大$O(mn)$
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 int  spfa (int  s)     memset (dist, 0x3f , sizeof  dist);     dist[s] = 0 ;          q.emplace (0 , s);     vis[s] = 1 ;     while (!q.empty ()){         auto  u = q.front ().second;         q.pop ();         vis[u] = 0 ;                  for (auto  &v: g[u]){             if (dist[v.second] > dist[u] + v.first){                 dist[v.second] = dist[u] + v.first;                               if (!vis[v.second]){                     q.emplace (dist[v.second], v.second);                     vis[v.second] = 1 ;                 }                              }         }     }     return  dist[n]; } 
4. Floyd 邻接表存图,可处理负权边,时间复杂度$O(n^3)$
1 2 3 4 5 6 7 8 9 10 11 void  floyd ()     for (int  k = 1 ; k <= n; k++){         for (int  i = 1 ; i <= n; i++){             for (int  j = 1 ; j <= n; j++){                 d[i][j] = min (d[i][j], d[i][k] + d[k][j]);             }         }     } } 
最小生成树 1.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 int  g[N][N]; int  dist[N]; bool  vis[N]; int  prim ()     memset (dist, 0x3f , sizeof  dist);     dist[1 ] = 0 ;     int  res = 0 ;      for (int  i = 0 ; i < n; i++)     {         int  t = -1 ;          for (int  j = 1 ; j <= n; j++)             if (!vis[j] && (t == -1  || dist[t] > dist[j]))                 t = j;         if (dist[t] == INF) return  INF;         res += dist[t];         vis[t] = 1 ;         for (int  j = 1 ; j <= n; j++)             dist[j] = min (dist[j], g[t][j]);     }     return  res; } 
堆优化版$O(mlogn)$,一般用于稀疏图(X) 
 
(此时用$Kruskal$更方便)
2.Kruskal $O(mlogm)$,一般用于稀疏图
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 struct  Edge {    int  a, b, w; }edges[M]; bool  cmp (Edge &x, Edge &y)     return  x.w < y.w; } int  kruskal ()     sort (edges, edges + m, cmp);     for  (int  i = 1 ; i <= n; i ++ ) p[i] = i;         int  res = 0 , cnt = 0 ;     for  (int  i = 0 ; i < m; i ++ )     {         int  a = edges[i].a, b = edges[i].b, w = edges[i].w;         a = find (a), b = find (b);         if  (a != b)          {             p[a] = b;             res += w;             cnt ++ ;         }     }     if  (cnt < n - 1 ) return  INF;     return  res; } 
拓扑排序 	拓扑排序:将集合内已知的偏序关系整合成全序
具体流程如下:
将所有入度为0的点加入处理队列 
将处于队头的点x取出,遍历所有x能到达的点y 
对每一个y,处理入度-1 
y入度为0时,将y加入处理队列 
重复以上过程直到队列为空 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 bool  topo ()     int  cnt = 0 ;     for (int  i = 1 ; i <= n; i++)         if (!ind[i]) q.emplace (i);          while (!q.empty ()){         int  u = q.front ();         q.pop (); cnt++;         res.emplace_back (u);                  for (auto  &v : g[u]){             ind[v]--;             if (!ind[v])q.emplace (v);         }     }     return  cnt == n; } 
LCA 	Least Common Ancestors 最近公共祖先
树上倍增法:预处理O(nlogn) 每次查询O(logn)
	f[i][j]表示 i 节点向上走 $2^j$ 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void  dfs (int  u, int  fa)      for  (auto & v : g[u]) if (v != fa) {         dep[v] = dep[u] + 1 ;         f[v][0 ] = u;         for  (int  i = 1 ; i < M; i++)             f[v][i] = f[f[v][i - 1 ]][i - 1 ];         dfs (v, u);     }     return ; } int  lca (int  x, int  y)      if  (dep[x] < dep[y])swap (x, y);          for  (int  i = M - 1 ; i >= 0 ; i--) if  (dep[f[x][i]] >= dep[y])x = f[x][i];     if  (x == y)return  x;     for  (int  i = M - 1 ; i >= 0 ; i--) if  (f[x][i] != f[y][i])x = f[x][i], y = f[y][i];     return  f[x][0 ]; } 
Tarjan缩点 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 vector<int > g[N]; int  dfn[N], low[N], timestamp;int  stk[N], top; bool  in_stk[N];int  id[N], scc_cnt, siz[N];void  tarjan (int  u)     dfn[u] = low[u] = ++timestamp;     stk[++top] = u, in_stk[u] = 1 ;     for (auto  &v : g[u]){         if (!dfn[v]){             tarjan (v);             low[u] = min (low[u], low[v]);         }         else  if (in_stk[v]) low[u] = min (low[u], dfn[v]);     }          if (dfn[u] == low[u]){         ++scc_cnt;         int  y;         do {             y = stk[top--];             in_stk[y] = 0 ;             id[y] = scc_cnt;             siz[scc_cnt]++;         }while (y != u);     } } 
数学 素数判定 1 2 3 4 5 6 bool  is_Prime (int  n)     if (n < 2 ) return  0 ;     for (int  i = 2 ; i <= n / i; i++)         if (!(n % i)) return  0 ;     return  1 ; } 
埃氏筛 1 2 3 4 5 6 7 8 void  Eratosthenes (int  n)     for (int  i = 2 ; i <= n; i++){         if (!comp[i]){             primes[cnt++] = i;             for (int  j = i + i; j <= n; j += i)comp[j] = 1 ;         }     } } 
若是质数,加入质数表, 所有倍数判为合数。$O(nloglogn)$ 
 
欧拉筛(线性筛) 1 2 3 4 5 6 7 8 9 void  Euler (int  n)     for (int  i = 2 ; i <= n; i++){         if (!comp[i]) primes[cnt++] = i;         for (int  j = 0 ; primes[j] <= n / i; j++){             comp[i * primes[j]] = 1 ;             if (!(i % primes[j])) break ;         }     } } 
若是质数,加入质数表 
筛去i的质数倍,直到i成为某质数的倍数 
 
(n只会被最小质因子筛掉)
求所有约数 1 2 3 4 5 6 7 8 9 10 11 vector<int >get_divisors (int  n){     vector<int > res;     for (int  i = 1 ; i <= n / i; i++){         if (!(n % i)){             res.emplace_back (i);             if (i != n / i) res.emplace_back (n / i);         }        }     sort (res.begin (), res.end ());     return  res; } 
算数基本定理 任意自然数可唯一分解$N=\prod\limits_{i=1}^ka_i^k$
约数个数$\prod\limits_{i=1}^k(a_i+1)$ 		约数之和$\prod\limits_{i=1}^k\sum\limits_{j=0}^{a_i}a^j$
欧几里得算法 1 int  gcd (int  a, int  b) return  b ? gcd (b, a % b) : a;}
欧拉函数 欧拉函数:1到n中与n互质的数的个数
$\phi(n)=N(1-\prod\frac 1{p_i})$
筛法求欧拉函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void  get_phi (int  n)     phi[1 ] = 1 ;     for (int  i = 2 ; i <= n; i++){         if (!comp[i]) {             primes[cnt++] = i;             phi[i] = i - 1 ;         }         for (int  j = 0 ; primes[j] <= n / i; j++){             comp[i * primes[j]] = 1 ;             if (!(i % primes[j])){                 phi[i * primes[j]] = primes[j] * phi[i];                 break ;             }             phi[i * primes[j]] = (primes[j] - 1 ) * phi[i];         }     }     } 
三个转移情况:
i 为质数时$\phi(i)=i-1$ 
i 整除primes[ j ]时,$\phi(i*primes[j]) = primes[j] \times \phi(i)$ 
i 不整除primes[ j ]时,$\phi(i*primes[j]) = (primes[j]-1) \times \phi(i)$ 
 
欧拉定理 $$
快速幂 1 2 3 4 5 6 7 8 9 int  qmi (int  a, int  k, int  p)     int  res = 1 ;     while (k){         if (k & 1 ) res = res * a % p;         k >>= 1 ;          a = a * a % p;     }        return  res; } 
$O(\log(k))$ 的时间复杂度计算$a^k\  %p$
扩展欧几里得算法 1 2 3 4 5 6 7 8 9 int  exgcd (int  a, int  b, int  &x, int  &y)     if (!b){         x = 1 , y = 0 ;         return  a;     }     int  d = exgcd (b, a % b, y, x);     y -= a / b * x;     return  d; } 
动态规划 以下v表示volume, w表示worth
01背包 1 2 3 4 5 for (int  i = 1 ; i <= n ;i++){    for (int  j = V; j >= v[i]; j--){          dp[j] = max (dp[j], dp[j - v[i]] + w[i]);     } } 
完全背包 1 2 3 4 5 for (int  i = 1 ; i <= N; i++){    for (int  j = v[i]; j <= V; j++){          dp[j] = max (dp[j], dp[j - v[i]] + w[i]);     } } 
多重背包 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 int  cnt = 0 ;for (int  i = 1 ; i <= n; i++){    int  a, b, s;     cin >> a >> b >> s;     int  k = 1 ;     while (k <= s){         cnt++;         v[cnt] = a * k;         w[cnt] = b * k;         s -= k;         k *= 2 ;     }     if (s > 0 ){         cnt++;         v[cnt] = a * s;         w[cnt] = b * s;     } } n = cnt; for (int  i = 1 ; i <= n; i++){    for (int  j = V; j >= v[i]; j--){         dp[j] = max (dp[j], dp[j - v[i]] + w[i]);     } } 
分组背包 1 2 3 4 5 6 7 8 9 for (int  i = 1 ; i <= n; i++){    for (int  j = V; j >= 0 ; j--){         for (int  k = 0 ; k < s[i]; k++){             if (v[i][k] <= j){                 dp[j] = max (dp[j], dp[j - v[i][k]] + w[i][k]);             }         }     } } 
多维费用背包问题 简单加循环即可。