数论
欧拉筛
vector<int> ola(int n) {
vector<int> prime;
prime.reserve(n / 10);
vector<char> vis(n + 1);
prime.pb(2);
for (int i = 3; i <= n; i += 2) {
if (!vis[i]) prime.pb(i);
for (int j = 0; i * prime[j] <= n; ++ j) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
return prime;
}
快速幂
int quick_pow(int x, int n, int mod) {
int res = 1;
while (n > 0) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
矩阵快速幂
const int p = 1e9 + 7;
struct matrix {
ll c[101][101];
matrix() {memset(c, 0, sizeof(c));}
} A, res;
ll n, k;
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]) % p;
return t;
}
void quickpow(ll 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;
}
}
常见优化技巧
单调队列
void solve() {
int n, k; cin >> n >> k;
vi v(n);
for (auto &i: v) cin >> i;
deque<int> dq;
for (int i = 0; i < n; ++ i) {
while (sz(dq) && dq.front() + k <= i) dq.pop_front();
while (sz(dq) && v[i] > v[dq.back()]) dq.pop_back();
dq.push_back(i);
if (i >= k - 1) cout << v[dq.front()] << "\n";
}
}
排序
归并排序
const int N = 2e5 + 5;
class MS {
private:
int b[N];
public:
int ans = 0;
void msort(int l,int r, vector<int> &arr){
if(l >= r) return;
int mid = (l + r) >> 1;
msort(l, mid, arr);
msort(mid + 1,r,arr);
int i = l, j = mid + 1, k = l;
while(i <= mid && j <= r) {
if(arr[i] <= arr[j]) b[k++] = arr[i++];
else { b[k++] = arr[j++]; ans += mid - i + 1; }
}
while(i <= mid) b[k++] = arr[i++];
while(j <= r) b[k++] = arr[j++];
for(int i = l; i <= r; ++i) arr[i] = b[i];
}
};
字符串
KMP算法
vector<int> ne(1e6, 0);
void KMP(string S,string P){
ll n = P.size(), m = S.size();
for (int i = 1, j = 0; i < n; ++i) {
while(j && P[i] != P[j])
j = ne[j - 1];
if (P[i] == P[j])
j++;
ne[i] = j;
}
for (int i = 0, j = 0; i < m; ++i) {
while(j && S[i] != P[j])
j = ne[j - 1];
if (S[i] == P[j])
j++;
if(j == n){
cout << i - n + 2 << '\n';
j = ne[j - 1];
}
}
}
int main() {
string s1,s2;
cin >> s1 >> s2;
KMP(s1,s2);
for(int i=0;i<s2.size();++i) cout << ne[i] << " ";
return 0;
}
马拉车算法
const int N = 2e7 + 5;
char a[N];
char s[N << 1];
int d[N << 1];
int manacher(char *a) {
int n = strlen(a + 1), k = 0;
s[0] = '$', s[++k] = '#';
for(int i = 1; i <= n; ++ i) {
s[++k] = a[i];
s[++k] = '#';
}
n = k, d[1] = 1;
for(int i = 2, l, r = 1; i <= n; ++ i) {
if(i <= r) d[i] = min(d[r-i+l], r-i+1);
while(s[i-d[i]] == s[i+d[i]]) d[i]++;
if(i + d[i] - 1 > r) {
l = i - d[i] + 1;
r = i + d[i] - 1;
}
}
return *max_element(d, d + n) - 1;
}
void solve() {
scanf("%s", a+1);
cout << manacher(a);
}
动态规划
01背包
void solve1() {
int n, m; cin >> n >> m;
vi w(m + 1, 0), v(m + 1, 0);
vvi dp(m + 1, vi(n + 1, 0));
for (int i = 1; i <= m; ++ i)
cin >> w[i] >> v[i];
for (int i = 1; i <= m; ++ i) {
for (int j = 1; j <= n; ++ j) {
if (j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
}
}
cout << dp[m][n] << "\n";
}
void solve2() {
int n, m; cin >> n >> m;
vi w(m + 1, 0), v(m + 1, 0);
vi dp(n + 1, 0);
for (int i = 1; i <= m; ++ i)
cin >> w[i] >> v[i];
for (int i = 1; i <= m; ++ i) {
for (int j = n; j >= 1; -- j) {
if (j >= w[i]) {
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}
}
cout << dp[n] << "\n";
}
多重背包
void solve() {
vi vv, ww;
int n, m; cin >> n >> m;
int v, w, s;
for (int i = 1; i <= n; ++ i) {
cin >> v >> w >> s;
for (int j = 1; j <= s; j <<= 1) {
vv.pb(j * v);
ww.pb(j * w);
s -= j;
}
if (s) {
vv.pb(s * v);
ww.pb(s * w);
}
}
vi dp(m + 1);
for (int i = 0; i < sz(vv); ++ i) {
for (int j = m; j >= vv[i]; -- j) {
dp[j] = max(dp[j], dp[j-vv[i]] + ww[i]);
}
}
cout << dp[m];
}
二维费用背包
void solve() {
int n, H, S;
cin >> n >> H >> S;
int h, s, w;
vvi dp(H + 5, vi(S + 5));
for (int i = 1; i <= n; ++ i) {
cin >> h >> s >> w;
for (int j = H; j >= h; -- j) {
for (int k = S; k >= s; -- k) {
dp[j][k] = max(dp[j][k], dp[j-h][k-s] + w); }
}
}
cout << dp[H][S] << "\n";
}
分组背包
void solve() {
int n, V; cin >> n >> V;
vi v(int(2e5)), w(int(2e5));
int s;
vi dp(V + 1);
for (int i = 1; i <= n; ++ i) {
cin >> s;
for (int j = 1; j <= s; -- j) {
cin >> v[j] >> w[j];
}
for (int j = V; j >= 1; --j) {
for (int k = 0; k <= s; ++ k) {
if (j >= v[k]) {
dp[j] = max(dp[j], dp[j - v[k]] + w[k]);
}
}
}
}
cout << dp[V] << "\n";
}
混合背包
void solve() {
int n, m; cin >> n >> m;
vi a, b, c;
int v, w, s;
for (int i = 1; i <= n; ++ i) {
cin >> v >> w >> s;
if (s == 0) {
a.pb(v);b.pb(w);c.pb(0);
} else {
if (s == -1) s = 1;
int k = 1;
while (s >= k) {
a.pb(k * w);b.pb(k * w);c.pb(1);
s -= k;k <<= 1;
} if(s) {
a.pb(s * v);b.pb(s * w);c.pb(1);
}
}
}
vi dp(m);
for (int i = 1; i < sz(c); ++ i) {
if (c[i] == 1)
for (int j = m; j >= a[i]; -- j)
dp[j] = max(dp[j], dp[j-a[i]] + b[i]);
else
for (int j = a[i]; j <= m; ++ j)
dp[j] = max(dp[j], dp[j-a[i]] + b[i]);
}
cout << dp[m] << "\n";
}
完全背包
void solve() {
int n, m; cin >> n >> m;
vi t(m + 1), v(m + 1);
vvi dp(m + 1, vi(n + 1));
for(int i = 1; i <= m; ++ i) {
cin >> t[i] >> v[i];
}
for (int i = 1; i <= m; ++ i) {
for (int j = 1; j <= n; ++ j) {
dp[i][j] = max(dp[i-1][j], dp[i][j-t[i]] + v[i]);
}
}
cout << dp[m][n];
}
void solve2() {
int n, m;
cin >> n >> m;
vi t(m + 1), v(m + 1);
for (int i = 1; i <= m; ++ i) {
cin >> t[i] >> v[i];
}
vi dp(n + 1);
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++ j) {
if(j >= t[i])
dp[j] = max(dp[j], dp[j-t[i]] + v[i]);
}
}
cout << dp[n];
}
数据结构
并查集
struct DSU {
int par[N],rk[N];
void init(int n) {
for(int i = 1;i <= n;++i) par[i] = i, rk[i] = 0;
}
int find(int x) {
return par[x] = par[x] == x ? x : find(par[x]);
}
int unite(int x, int y) {
x = find(x);y = find(y);
return x == y ? 0 : par[x] = y;
}
void merge(int x, int y) {
x = find(x);y = find(y);
if(x == y) return;
if(rk[x] > rk[y]) swap(x, y);
par[x] = y;
if(rk[x] == rk[y]) rk[y]++;
}
bool same(int x, int y) {
return find(x) == find(y);
}
};
树的直径
const int N = 1e5 + 5;
vector<int> e[N];
int ans = 0, poi = 0;
int vis[N];
void dfs(int x, int dep) {
vis[x] = 1;
for(auto v : e[x]) {
if(!vis[v]) dfs(v, dep+1);
}
if(dep > ans) {
ans = dep;
poi = x;
}
}
void solve() {
int n; cin >> n;
int a, b;
for(int i=0;i<n-1;++i) {
cin >> a >> b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs(1, 1);
memset(vis, 0, sizeof(vis));
dfs(poi, 1);
cout << ans-1;
}
树链剖分-最近公共祖先
const int N = 5e5 + 5;
vector<int> G[N];
int fa[N], dep[N], son[N], sz[N];
int top[N];
void dfs1(int u, int father) {
fa[u] = father,dep[u] = dep[father] + 1,sz[u] = 1;
for(auto v : G[u]) {
if(v == father) continue;
dfs1(v, u);
sz[u] += sz[v];
if(sz[son[u]] < sz[v]) son[u] = v;
}
}
void dfs2(int u, int t) {
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);
}
}
int lca(int u, int v) {
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
void solve(){
int n, m, s; cin >> n >> m >> s;
for(int i=0;i<n-1;++i) {
int a, b; cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs1(s, 0);
dfs2(s ,s);
for(int i=0;i<m;++i) {
int u, v; cin >> u >> v;
cout << lca(u, v) << "\n";
}
}
树状数组
int n,m;
int a[MAXN],c[MAXN];
int lowbit(int x) {
return x & (-x);
}
int sum(int x) {
int res = 0;
for(int i = x; i ; i -= lowbit(i)) res += c[i];
return res;
}
void add(int x,int y) {
for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}
线段树
int n, w[N];
struct node {
int l, r, sum, add;
}tr[N*4];
void pushup(int p) {
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int p) {
if(tr[p].add) {
tr[lc].sum += (tr[lc].r - tr[lc].l + 1) * tr[p].add;
tr[rc].sum += (tr[rc].r - tr[rc].l + 1) * tr[p].add;
tr[lc].add += tr[p].add;
tr[rc].add += tr[p].add;
tr[p].add = 0;
}
}
void build(int p, int l, int r) {
tr[p] = {l, r, w[l], 0};
if(l == r) return;
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
pushup(p);
}
void update(int p, int x, int y, int 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;
}
int 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);
}
int query(int p, int x, int y) {
if(x <= tr[p].l && tr[p].r <= y) return tr[p].sum;
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
int sum = 0;
if(x <= m) sum += query(lc, x, y);
if(y > m) sum += query(rc, x, y);
return sum;
}
void update(int p, int x, int k) {
if(tr[p].l == x && tr[p].r == x) {
tr[p].sum = k;
return;
}
int m = (tr[p].l + tr[p].r) >> 1;
if(x <= m) update(lc, x, k);
if(x > m) update(rc, x, k);
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
int get(int x) {
return query(1, x, x);
}
字典树
#define MAXN 2000 * 75 + 5
int trie[MAXN][26];
int word[MAXN];
char str[80];
int n, tot = 0,ans = 0;
void insert (char *str) {
int u = 0;
int len = strlen(str);
int res = 0;
for (int i = 0; i < len;++i) {
int a = str[i] - 'a';
if (trie[u][a] == 0)
trie[u][a] = ++tot;
u = trie[u][a];
res += word[u];
}
word[u]++;
if (res + 1 > ans)
ans = res + 1;
}
ST表
inline int gcd(int a,int b) {
return b == 0 ? a : gcd(b, a % b);
}
inline int lcm(int a,int b) {
return (a / gcd(a, b)) * b % mod;
}
const int MAXN = 2e5 + 5;
const int LOG = 18;
int lcm_arr[MAXN][LOG];
int log_2[MAXN + 1];
void buildST(vector<int>& arr) {
int n = arr.size();
log_2[1] = 0;
for (int i = 2; i <= n; ++i) {
log_2[i] = log_2[i / 2] + 1;
}
for (int i = 1; i <= n; ++i) {
lcm_arr[i][0] = arr[i];
}
for (int j = 1; (1 << j) <= n; ++j) {
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
lcm_arr[i][j] = lcm(lcm_arr[i][j - 1], lcm_arr[i + (1 << (j - 1))][j - 1]) % mod;
}
}
}
int query_lcm(int L, int R) {
int len = R - L + 1;
int j = log_2[len];
return lcm(lcm_arr[L][j], lcm_arr[R - (1 << j) + 1][j]);
}
图论
dijkstra最短路
struct Dijkstra {
struct Edge { int v, w; };
int n;
vector<vector<Edge>> G;
vector<int> d, vis, path, cnt;
Dijkstra(int _n): n(_n) {
G.assign(n + 1, {});
d.assign(n + 1, inf);
vis.assign(n + 1, 0);
path.assign(n + 1, -1);
cnt.assign(n + 1, 0);
}
void add(int u, int v, int w) {
G[u].push_back({v, w});
}
void dijkstra(int s) {
d[s] = 0;
cnt[s] = 1;
using pii = pair<int, int>;
priority_queue<pii, vector<pii>, greater<>> pq;
pq.push({0, s});
while (!pq.empty()) {
auto [_u, u] = pq.top(); pq.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto &e : G[u]) {
int v = e.v, w = e.w;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
path[v] = u;
cnt[v] = cnt[u];
pq.push({d[v], v});
} else if (d[v] == d[u] + w) {
cnt[v] += cnt[u];
}
}
}
}
vector<int> getPath(int u, int s) {
vector<int> res;
if (d[u] == inf) return res;
for (int cur = u; cur != -1; cur = path[cur]) {
res.push_back(cur);
if (cur == s) break;
}
reverse(res.begin(), res.end());
return res;
}
};
拓扑排序
const int N = 105;
vector<int> G[N], tp;
int din[N];
int n;
bool toposort() {
queue<int> q;
for(int i=1;i<=n;++i) {
if(din[i]==0) q.push(i);
}
while(!q.empty()) {
int x = q.front();
q.pop();
tp.push_back(x);
for(auto c : G[x]) {
if(--din[c] == 0) q.push(c);
}
}
return tp.size() == n;
}
二分图判定-染色法
const int M = 1005, N = 1e5;
struct edge {
int v, w, ne;
}e[M];
int idx, h[N], color[N];
void add(int a, int b, int w) {
e[idx] = {b, w, h[a]};
h[a] = idx++;
}
bool dfs(int u, int c) {
color[u] = c;
for(int i=h[u];i;i=e[i].ne) {
int v = e[i].v;
if(!color[v]) {
if(dfs(v, 3-c)) return 1;
}
else if(color[v] == c) return 1;
}
return 0;
}
bool flag = 0;
bool check(int n) {
for(int i=1;i<=n;++i) {
if(!color[i]) {
if(dfs(i,1)) {
flag = 1;
break;
}
}
}
if(flag) return 0;
else return 1;
}
二分图最大匹配-匈牙利算法
int n, m, k, ans;
const int M = 1005, N = 1e5 + 5;
struct edge {
int v, ne;
}e[N];
int idx, h[N];
int vis[N], match[N];
void add(int a, int b) {
e[++idx] = {b, h[a]};
h[a] = idx;
}
bool dfs(int u) {
for(int i=h[u];i;i=e[i].ne) {
int v = e[i].v;
if(vis[v]) continue;
vis[v] = 1;
if(!match[v] || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
强连通分量
const int N = 1e5 + 5 ;
vector<int> e[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top;
int scc[N], siz[N], cnt;
void tarjan(int x) {
dfn[x] = low[x] = ++tot;
stk[++top] = x, instk[x] = 1;
for(int y : e[x]) {
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if(instk[y])
low[x] = min(low[x], dfn[y]);
}
if(dfn[x] == low[x]) {
int y; ++cnt;
do {
y = stk[top--]; instk[y] = 0;
scc[y] = cnt;
++siz[cnt];
}while(y != x);
}
}
最小生成树
class DSU {
private:
int par[N],rk[N];
public:
void init(int n) {
for(int i = 1;i <= n;++i) par[i] = i, rk[i] = 0;
}
int find(int x) {
return par[x] = par[x] == x ? x : find(par[x]);
}
int unite(int x, int y) {
x = find(x);y = find(y);
return x == y ? 0 : par[x] = y;
}
void merge(int x, int y) {
x = find(x);y = find(y);
if(x == y) return;
if(rk[x] > rk[y]) swap(x, y);
par[x] = y;
if(rk[x] == rk[y]) rk[y]++;
}
bool same(int x, int y) {
return find(x) == find(y);
}
};
class MST : DSU {
private:
struct edge{
int u, v, cost;
bool operator<(const edge& other) const {
return cost < other.cost;
}
};
vector<edge> es;
vector<edge> mst;
int V, res;
public:
MST(int n) : V(n) {}
int kruskal() {
sort(es.begin(), es.end());
init(V);
mst.clear();
res = 0;
for(auto &e : es) {
if(!same(e.u, e.v)) {
unite(e.u, e.v);
mst.push_back(e);
res += e.cost;
}
}
return res;
}
void addedge(int u, int v, int cost) {
es.push_back({u, v, cost});
}
bool isConnected() {
set<int> st;
for (int i = 1; i <= V; ++i) {
st.insert(find(i));
}
return st.size() == 1;
}
};
FHQ Treap
struct node {
int l, r;
int val;
int key;
int sz;
}tree[N];
int n, root, idx;
int newnode(int v) {
tree[++idx].val = v;
tree[idx].key = rand();
tree[idx].sz = 1;
return idx;
}
void pushup(int p) {
tree[p].sz = tree[tree[p].l].sz
+ tree[tree[p].r].sz + 1;
}
void split(int p, int v, int &x, int &y) {
if(!p) {
x = y = 0;
return;
}
if(tree[p].val <= v) {
x = p;
split(tree[x].r, v, tree[x].r, y);
} else {
y = p;
split(tree[y].l, v, x, tree[y].l);
}
pushup(p);
}
int merge(int x, int y) {
if(!x || !y) return x + y;
if(tree[x].key < tree[y].key) {
tree[x].r = merge(tree[x].r, y);
pushup(x);
return x;
} else {
tree[y].l = merge(x, tree[y].l);
pushup(y);
return y;
}
}
void insert(int v) {
int x, y, z;
split(root, v, x, y);
z = newnode(v);
root = merge(merge(x, z), y);
}
void del(int v) {
int x, y, z;
split(root, v, x, z);
split(x, v-1, x, y);
y = merge(tree[y].l, tree[y].r);
root = merge(merge(x, y), z);
}
int get_k(int p, int k) {
if(k <= tree[tree[p].l].sz)
return get_k(tree[p].l, k);
if(k == tree[tree[p].l].sz + 1)
return p;
return get_k(tree[p].r, k-tree[tree[p].l].sz-1);
}
Tarjan算法-最近公共祖先
const int N = 5e5 + 5;
vector<int> G[N];
vector<pair<int,int>> q[N];
int fa[N], vis[N], ans[N];
int find(int x) {
return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void tarjan(int u) {
vis[u] = true;
for(auto v : G[u]) {
if(!vis[v]) {
tarjan(v);
fa[v] = u;
}
}
for(auto qu : q[u]) {
int v = qu.first, i = qu.second;
if(vis[v]) ans[i] = find(v);
}
}