模板汇总
也许是中学生涯最后一次打板子了呀...
KMP模式匹配
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 1000005; char a[N], b[N]; int a1, b1, Next[N]; int main(){ cin >> a + 1; cin >> b + 1; a1 = strlen(a + 1); b1 = strlen(b + 1); Next[1] = 0; for(int i = 2, j = 0; i <= b1; i++){ while( j > 0 && b[j + 1] != b[i] ) j = Next[j]; if( b[i] == b[j + 1] ) j++; Next[i] = j; } for(int i = 1, j = 0; i <= a1; i++){ while( j > 0 && a[i] != b[j + 1]) j = Next[j]; if(a[i] == b[j + 1]) j++; if(j == b1) { cout << i - j + 1 <<"\n"; j = Next[j]; } } for(int i = 1; i <= b1; i++) cout << Next[i] <<" "; return 0; }
Trie字典树
#include<iostream> #include<cstdio> using namespace std; int Trie[N][27], end[p] = 1, tot = 1; void insert(char* str){ int len = strlen(str), p = 1; for(int k = 0; k < len; k++){ int ch = str[k] - 'a'; if(trie[p][ch] == 0) trie[p][ch] = ++tot; p = trie[p][ch]; } end[p] = 1; } bool search(char* str){ int len = strlen(str), p = 1; for(int k = 0; k < len; k++){ p = trie[p][str[k]]; if(p == 0) return 0; } return end[p]; }
ST表
值得注意的是,log函数最好配强制类型转换(不然poj会编译错误),转换的时候要用括号把log全部括一下,初始化的时候k要加一。初始化的时候j从0开始,我被坑了无数次了
#include<iostream> #include<cstdio> #include<cmath> using namespace std; const int N = 100005; int a[N][30], n, m, k; int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } int main(){ cin >> n >> m; for(int i = 1; i <= n; i++) a[i][0] = read(); k = int(log(n) / log(2) + 1); for(int i = 1; i <= k; i++) for(int j = 0; j <= n - (1 << i) + 1; j++) a[j][i] = max(a[j][i - 1], a[j + (1 << (i-1))][i - 1]); while(m--){ int x = read(), y = read(); k = int(log(y - x + 1) / log(2)); cout << max(a[x][k], a[y - (1 << k) + 1][k]) << "\n"; } return 0; }
单源最短路径 \(dijkstra\)
注意打标记和判重都在取出队头的时候进行
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N = 500005; int ver[N], head[N], Next[N], edge[N], tot; int n, m, s; void add(int x, int y, int z){ ver[++tot] = y, edge[tot] = z; Next[tot] = head[x], head[x] = tot; } int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } /*Dijkstra*/ int d[N], v[N]; void dijkstra(int s){ memset(d, 0x3f, sizeof d); memset(v, 0, sizeof v); priority_queue< pair< int, int > > q; q.push(make_pair(0, s)); d[s] = 0; while(q.size()){ int x = q.top().second; q.pop(); if(v[x]) continue; v[x] = 1; for(int i = head[x]; i; i = Next[i]){ int y = ver[i], z = edge[i]; if(d[y] > d[x] + z){ d[y] = d[x] + z; q.push(make_pair(-d[y], y)); } } } } int main(){ cin >> n >> m >> s; for(int i = 1; i <= m; i++){ int x = read(), y = read(), z = read(); add(x, y, z); } dijkstra(s); for(int i = 1; i <= n; i++){ if(d[i] == 0x3f3f3f3f) cout << "2147483647 "; else cout << d[i] <<" "; } return 0; }
单源最短路径\(SPFA\)
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N = 500005; int ver[N], head[N], Next[N], edge[N], tot; int n, m, s; void add(int x, int y, int z){ ver[++tot] = y, edge[tot] = z; Next[tot] = head[x], head[x] = tot; } int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } int d[N], v[N]; /* spfa */ void spfa(int s){ memset(d, 0x3f, sizeof d); memset(v, 0, sizeof v); queue<int> q; q.push(s); v[s] = 1; d[s] = 0; while(q.size()){ int x = q.front(); q.pop(); v[x] = 0; for(int i = head[x]; i; i = Next[i]){ int y = ver[i], z = edge[i]; if(d[y] > d[x] + z){ d[y] = d[x] + z; if(!v[x]) q.push(y), v[y] = 1; } } } } int main(){ cin >> n >> m >> s; for(int i = 1; i <= m; i++){ int x = read(), y = read(), z = read(); add(x, y, z); } spfa(s); for(int i = 1; i <= n; i++){ if(d[i] == 0x3f3f3f3f) cout << "2147483647 "; else cout << d[i] <<" "; } return 0; }
扩展欧几里得
\(x = \frac{c}{d}x_0 + k \frac{b}{d}, y = \frac {c}{d}y_0 - k \frac{a}{d}\)
#include<iostream> #define ll long long using namespace std; int exgcd1(int a, int b, int &x, int &y){ if(b == 0) { x = 1, y = 0; return a; } int d = exgcd1(b, a % b, x, y); int z = x; x = y, y = z - (a / b) * y; return d; } ll exgcd(ll a, ll b, ll &x, ll &y){ if(b == 0){ x = 1, y = 0; return a; } int d = exgcd(b, a % b, x, y); int z = x; x = y, y = z - (a / b) * y; return d; } int main(){ ll a, b, x, y, d; cin >> a >> b; d = exgcd(a, b, x, y); cout << (x % (b / d) + (b / d)) % (b / d) <<"\n"; return 0; }
最近公共祖先 LCA
- 树上倍增法
敲这个板子出了一堆错误。所以要记得空间开到两倍,lca的大于等于号。错的太智障了
#include<iostream> #include<cstdio> #include<queue> #include<cmath> #include<algorithm> using namespace std; const int N = 1000005; int ver[N], head[N], Next[N], tot; int n, m, s, k, f[N][25], d[N]; void add(int x, int y){ ver[++tot] = y, Next[tot] = head[x], head[x] = tot; } void bfs(){ queue<int> q; q.push(s); d[s] = 1; while(q.size()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = Next[i]){ int y = ver[i]; if(d[y]) continue; d[y] = d[x] + 1; f[y][0] = x; for(int j = 1; j <= k; j++) f[y][j] = f[f[y][j - 1]][j - 1]; q.push(y); } } } int lca(int x, int y){ if(d[y] < d[x]) swap(x, y); for(int i = k; i >= 0; i--) if(d[f[y][i]] >= d[x]) y = f[y][i]; if(x == y) return x; for(int i = k; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } int main(){ cin >> n >> m >> s; k = (int)( log(n) / log(2) ) + 1; for(int i = 1; i < n; i++) { int x = read(), y = read(); add(x, y), add(y, x); } bfs(); while(m--){ int x = read(), y = read(); cout << lca(x, y) <<"\n"; } return 0; }
- 树链剖分法
#include<iostream> #include<cstdio> using namespace std; const int N = 1000005; int ver[N], head[N], Next[N], tot; int d[N], son[N], f[N], top[N], v[N], size[N]; int n, m, s; void add(int x, int y){ ver[++tot] = y, Next[tot] = head[x], head[x] = tot; } void dfs1(int x){ v[x] = 1; size[x] = 1; for(int i = head[x]; i; i = Next[i]){ int y = ver[i]; if(v[y]) continue; d[y] = d[x] + 1; dfs1(y); f[y] = x; size[x] += size[y]; if(!son[x] || size[y] > size[son[x]]) son[x] = y; } } void dfs2(int x, int p){ top[x] = p; if(son[x]) dfs2(son[x], p); for(int i = head[x]; i; i = Next[i]){ int y = ver[i]; if(y == f[x] || y == son[x]) continue; dfs2(y, y); } } int lca(int x, int y){ while(top[x] != top[y]) if(d[top[x]] > d[top[y]]) x = f[top[x]]; else y = f[top[y]]; if(d[x] > d[y]) return y; return x; } int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } int main(){ cin >> n >> m >> s; for(int i = 1; i < n; i++) { int x = read(), y = read(); add(x, y), add(y, x); } d[s] = 1; dfs1(s); dfs2(s, s); while(m--){ int x = read(), y = read(); cout << lca(x, y) <<"\n"; } return 0; }
最小生成树 Kruskal
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 200005; int n, m, f[N], ans = 0; struct node{ int x, y, z; }edge[N]; bool cmp(node a, node b){ return a.z < b.z; } int get(int x){ if(x == f[x]) return x; return f[x] = get(f[x]); } int main(){ cin >> n >> m; for(int i = 1; i <= n; i++) f[i] = i; for(int i = 1; i <= m; i++){ cin >> edge[i].x >> edge[i].y >> edge[i].z; } sort(edge + 1, edge + m + 1, cmp); for(int i = 1; i <= m; i++){ int x = edge[i].x, y = edge[i].y; x = get(x), y = get(y); if(x == y) continue; f[x] = y; ans += edge[i].z; } cout << ans; return 0; }
树的直径
void dp(int x){ v[x] = 1; for(int i = head[x]; i; i = Next[i]){ int y = ver[i]; if(v[y]) continue; dp(y); ans = max(ans, d[x] + d[y] + edge[i]); d[x] = max(d[x], d[y] + edge[i]); } }
树状数组
OI生涯学的第一个数据结构!
板子超好写,难在分析题目中能用前缀和、差分解决的地方。
void update(int x, int y){ for(; x <= n; x += x & -x) s[x] += y; } int getsum(int x){ int ans = 0; for(; x; x -= x & -x) ans += s[x]; return ans; }
归并排序
\(mid - i + 1\)背下来就好啦
#include<iostream> using namespace std; int a[100005], b[100005], ans; void merge_sort(int l, int r){ if(l == r) return; int mid = (l + r) >> 1; merge_sort(l, mid); merge_sort(mid + 1, r); int i = l, j = mid + 1, k = l; while(i <= mid || j <= r){ if(j > r || ( a[i] <= a[j] && i <= mid )) b[k++] = a[i++]; else b[k++] = a[j++], ans += mid - i + 1; } for(int i = l; i <= r; i++) a[i] = b[i]; } int main(){ int n; cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; merge_sort(1, n); cout << ans; return 0; }
乘法逆元
#include<iostream> #include<cstdio> using namespace std; const int N = 3000005; long long a[N], n, p; int main(){ cin >> n >> p; a[0] = a[1] = 1; cout << 1 <<"\n"; for(int i = 2; i <= n; i++){ a[i] = (p - p / i) * a[p % i] % p; cout << a[i] <<"\n"; } return 0; }
卢卡斯定理
#include<iostream> #include<cstdio> using namespace std; const int N = 1000005; long long a[N]; long long n, m, p; long long fp(long long a, long long b){ long long ans = 1; for(; b; b >>= 1){ if(b & 1) ans = ans * a % p; a = a * a % p; } return ans; } long long C(long long n, long long m){ if(n < m) return 0; return (a[n] * fp(a[m], p - 2)) % p * fp(a[n - m], p - 2) % p ; } long long Lucas(long long n, long long m){ if(m == 0) return 1; return C(n % p, m % p) * Lucas(n / p, m / p) % p; } int read(){ int x = 0, ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } int main(){ // for(int i = 2; i < p; i++) a[i] = (p - p / i) * a[p % i] % p; int t = read(); while(t--){ n = read(), m = read(), p = read(); a[0] = 1; for(int i = 1; i <= p; i++) a[i] = a[i - 1] * i % p; cout << Lucas(n + m, n) <<"\n"; } return 0; }
线段树
一个暑假的记忆,大概是我最有把握的模板了吧。
#include<iostream> #include<cstdio> using namespace std; const int SIZE = 100005; struct SegmentTree{ int l, r; long long sum, add; }t[SIZE * 4]; int a[SIZE]; void build(int p, int l, int r){ t[p].l = l, t[p].r = r; if(l == r) { t[p].sum = a[l]; return ; } int mid = (l + r) >> 1; build(p*2, l, mid); build(p*2+1, mid + 1, r); t[p].sum = t[p*2].sum + t[p*2+1].sum; } void spread(int p){ if(t[p].add){ t[p*2].sum += (long long)(t[p*2].r - t[p*2].l + 1) * t[p].add; t[p*2+1].sum += (long long)(t[p*2+1].r - t[p*2+1].l + 1) * t[p].add; t[p*2].add += t[p].add; t[p*2+1].add += t[p].add; t[p].add = 0; } } void change(int p, int l, int r, int val){ if(l <= t[p].l && r >= t[p].r){ t[p].add += val; t[p].sum += (long long)(t[p].r - t[p].l + 1) * val; return; } spread(p); int mid = (t[p].l + t[p].r) >> 1; if(l <= mid) change(p*2, l, r, val); if(r > mid) change(p*2+1, l, r, val); t[p].sum = t[p*2].sum + t[p*2+1].sum; } long long ask(int p, int l, int r){ if(l <= t[p].l && r >= t[p].r) return t[p].sum; spread(p); int mid = (t[p].l + t[p].r) >> 1; long long val = 0; if(l <= mid) val += ask(p*2, l, r); if(r > mid) val += ask(p*2+1, l, r); return val; } int main(){ int n, m; cin >> n >> m; for(int i = 1; i <= n; i++) cin >> a[i]; build(1, 1, n); while(m--){ int f, x, y, h; cin >> f >> x>> y; if(f == 1){ cin >> h; change(1, x, y, h); } if(f == 2){ cout << ask(1, x, y) <<"\n"; } } return 0; }
并查集
板子不重要,路径压缩和带权才重要
int get(int x){ if(x == fa[x]) return x; return fa[x] = get(fa[x]); } void merge(int x, int y){ fa[get(x)] = get(y); }
平衡树Treap
#include<iostream> #include<cstdlib> using namespace std; const int SIZE = 100005; struct Treap{ int l, r; int val, dat; int size, cnt; }a[SIZE]; int tot, root, n, INF=0x7fffffff; int New(int val){ a[++tot].val = val; a[tot].dat = rand(); a[tot].size = a[tot].cnt = 1; return tot; } void Update(int p){ a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt; } void Build(){ New(-INF), New(INF); root = 1, a[1].r = 2; Update(root); } void zig(int &p){ int q = a[p].l; a[p].l = a[q].r, a[q].r = p, p = q; Update(a[p].r), Update(p); } void zag(int &p){ int q = a[p].r; a[p].r = a[q].l, a[q].l = p, p = q; Update(a[p].l), Update(p); } void Insert(int &p, int val){ if(p == 0){ p = New(val); return; } if(a[p].val == val){ a[p].cnt++, Update(p); return; } if(val < a[p].val){ Insert(a[p].l, val); if(a[p].dat < a[a[p].l].dat) zig(p); } else{ Insert(a[p].r, val); if(a[p].dat < a[a[p].r].dat) zag(p); } Update(p); } int GetRankByVal(int p, int val){ if(p == 0) return 0; if(val == a[p].val) return a[a[p].l].size; if(val < a[p].val) return GetRankByVal(a[p].l, val); return a[a[p].l].size + a[p].cnt + GetRankByVal(a[p].r, val); } int GetValByRank(int p, int rank){ if(p == 0) return INF; if(a[a[p].l].size >= rank) return GetValByRank(a[p].l, rank); if(a[a[p].l].size + a[p].cnt >= rank) return a[p].val; return GetValByRank(a[p].r, rank - a[a[p].l].size - a[p].cnt); } int GetPrev(int val){ int ans = 1; int p = root; while(p){ if(val == a[p].val){ if(a[p].l > 0){ p = a[p].l; while(a[p].r > 0) p = a[p].r; ans = p; } break; } if(a[ans].val < a[p].val && a[p].val < val) ans = p; p = val < a[p].val ? a[p].l : a[p].r; } return a[ans].val; } int GetNext(int val){ int ans = 2; int p = root; while(p){ if(val == a[p].val){ if(a[p].r > 0){ p = a[p].r; while(a[p].l > 0) p = a[p].l; ans = p; } break; } if(a[ans].val > a[p].val && a[p].val > val) ans = p; p = val < a[p].val ? a[p].l : a[p].r; } return a[ans].val; } void Remove(int &p, int val){ if(p == 0) return; if(val == a[p].val){ if(a[p].cnt > 1){ a[p].cnt--, Update(p); return; } if(a[p].l || a[p].r){ if(a[p].r == 0 && a[a[p].l].dat > a[a[p].r].dat) zig(p), Remove(a[p].r, val); else zag(p), Remove(a[p].l, val); Update(p); }else p = 0; return; } val < a[p].val ? Remove(a[p].l, val) : Remove(a[p].r, val); Update(p); } int main(){ Build(); cin >> n; while(n--){ int opt, x; cin >> opt >> x; switch(opt){ case 1: Insert(root, x); break; case 2: Remove(root, x); break; case 3: cout << GetRankByVal(root, x) - 1 <<"\n"; break; case 4: cout << GetValByRank(root, x+1) <<"\n"; break; case 5: cout << GetPrev(x) <<"\n"; break; case 6: cout << GetNext(x) <<"\n"; break; } } return 0; }
平衡树Splay
#include<iostream> using namespace std; const int _ = 100005; const int INF = 0x7fffffff; int n, opt, x; struct Spaly{ int root, tot, cnt[_], val[_], size[_], ch[_][2], fa[_]; bool chk(int x){ return ch[fa[x]][1] == x; } void pushup(int x){ if(!x) return; size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; } int newnode(int x){ val[++tot] = x; cnt[tot] = size[tot] = 1; ch[x][0] = ch[x][1] = 0; return tot; } void rotate(int x){ int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k^1]; ch[y][k] = w, fa[w] = y; ch[z][chk(y)] = x, fa[x] = z; ch[x][k^1] = y, fa[y] = x; pushup(y), pushup(x); } void splay(int x, int goal = 0){ while(fa[x] != goal){ int y = fa[x], z = fa[y]; if(z != goal){ if(chk(x) == chk(y)) rotate(y); else rotate(x); } rotate(x); } if(!goal) root = x; } void insert(int x){ int cur = root, p = 0; while(cur && x != val[cur]) p = cur, cur = ch[cur][x > val[cur]]; if(cur) ++cnt[cur], splay(cur); else cur = ch[p][x > val[p]] = newnode(x), fa[cur] = p, splay(cur); } void find(int x){ if(!root) return; int cur = root; while(ch[cur][x > val[cur]] && x != val[cur]) cur = ch[cur][x > val[cur]]; splay(cur); } int rank(int x){ find(x); return cnt[ch[root][0]] + 1; } int kth(int k){ int cur = root; while(1){ if(ch[cur][0] && k <= size[ch[cur][0]]) cur = ch[cur][0]; else if(k > size[ch[cur][0]] + cnt[cur]) k -= size[ch[cur][0]] + cnt[cur], cur = ch[cur][1]; else return cur; } } int pre(int x){ find(x); if(val[root] < x) return root; int cur = ch[root][0]; while(ch[cur][1]) cur = ch[cur][1]; return cur; } int next(int x){ find(x); if(val[root] > x) return root; int cur = ch[root][1]; while(ch[cur][0]) cur = ch[cur][0]; return cur; } void remove(int x){ int u = pre(x), v = next(x); splay(u), splay(v, u); int w = ch[v][0]; if(cnt[w] > 1) --cnt[w], splay(w); else size[0] = cnt[0] = ch[v][0] = 0; } void maintain(int x){ if(!ch[x][0] && !ch[x][1]) size[x] = cnt[x]; if(ch[x][0]) maintain(ch[x][0]); if(ch[x][1]) maintain(ch[x][1]); pushup(x); } }T; int main(){ T.insert(-INF), T.insert(INF); cin >> n; for(int i=1; i<=n; i++){ cin >> opt >> x; if(opt == 1) T.insert(x); if(opt == 2) T.remove(x); if(opt == 3) cout << T.rank(x) - 1 <<"\n"; if(opt == 4) cout << T.val[T.kth(x)+1] <<"\n"; if(opt == 5) cout << T.val[T.pre(x)] <<"\n"; if(opt == 6) cout << T.val[T.next(x)] <<"\n"; } return 0; }