模板集合
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板集合相关的知识,希望对你有一定的参考价值。
读入优化
1 inline int read() 2 { 3 int x = 0, f = 1; 4 char ch = getchar(); 5 while(!isdigit(ch)) 6 { 7 if(ch == ‘-‘) f = -1; 8 ch = getchar(); 9 } 10 while(isdigit(ch)) 11 { 12 x = x * 10 + ch - ‘0‘; 13 ch = getchar(); 14 } 15 return x * f; 16 }
树状数组(单点修改)
1 #include <cstdio> 2 3 using namespace std; 4 5 int n, m; 6 int a[500001], c[500001]; 7 8 int lowbit(int x) 9 { 10 return x & -x; 11 } 12 13 int sum(int x) 14 { 15 int ans = 0; 16 while(x) 17 { 18 ans += c[x]; 19 x -= lowbit(x); 20 } 21 return ans; 22 } 23 24 void add(int x, int d) 25 { 26 while(x <= n) 27 { 28 c[x] += d; 29 x += lowbit(x); 30 } 31 } 32 33 int main() 34 { 35 int i, j, x, y, z; 36 scanf("%d %d", &n, &m); 37 for(i = 1; i <= n; i++) 38 { 39 scanf("%d", &a[i]); 40 add(i, a[i]); 41 } 42 for(i = 1; i <= m; i++) 43 { 44 scanf("%d %d %d", &z, &x, &y); 45 if(z == 1) add(x, y); 46 else printf("%d\n", sum(y) - sum(x - 1)); 47 } 48 return 0; 49 }
树状数组(区间修改)
1 #include <cstdio> 2 #include <iostream> 3 4 using namespace std; 5 6 int n, m; 7 long long c0[100001], c1[100001], a[100001]; 8 9 long long lowbit(int x) {return x & -x;} 10 11 long long sum(long long *c, int x) 12 { 13 long long ans = 0; 14 while(x) 15 { 16 ans += c[x]; 17 x -= lowbit(x); 18 } 19 return ans; 20 } 21 22 void add(long long *c, int x, int d) 23 { 24 while(x <= n) 25 { 26 c[x] += d; 27 x += lowbit(x); 28 } 29 } 30 31 int main() 32 { 33 int i, j, x, y, z, k; 34 long long ans; 35 scanf("%d%d", &n, &m); 36 for(i = 1; i <= n; i++) 37 { 38 scanf("%d", &a[i]); 39 add(c0, i, a[i]); 40 } 41 for(i = 1; i <= m; i++) 42 { 43 scanf("%d%d%d", &z, &x, &y); 44 if(z == 1) 45 { 46 scanf("%d", &k); 47 add(c0, x, -k * (x - 1)); 48 add(c1, x, k); 49 add(c0, y + 1, k * y); 50 add(c1, y + 1, -k); 51 } 52 else 53 { 54 ans = 0; 55 ans += sum(c0, y) + sum(c1, y) * y; 56 ans -= sum(c0, x - 1) + sum(c1, x - 1) * (x - 1); 57 printf("%lld\n", ans); 58 } 59 } 60 return 0; 61 }
线段树
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 5 using namespace std; 6 7 #define root 1, 1, N 8 #define ls o << 1, l, m 9 #define rs o << 1 | 1, m + 1, r 10 11 int L, R; 12 long long add[1500005], mul[1500005], c[1500005], P; 13 14 inline int read() 15 { 16 int x = 0, f = 1; 17 char ch = getchar(); 18 while(!isdigit(ch)) 19 { 20 if(ch == ‘-‘) f = -1; 21 ch = getchar(); 22 } 23 while(isdigit(ch)) 24 { 25 x = x * 10 + ch - ‘0‘; 26 ch = getchar(); 27 } 28 return x * f; 29 } 30 31 inline void pushup(int o) 32 { 33 c[o] = (c[o << 1] + c[o << 1 | 1]) % P; 34 } 35 36 inline void build(int o, int l, int r) 37 { 38 add[o] = 0; 39 mul[o] = 1; 40 if(l == r) 41 { 42 scanf("%lld", &c[o]); 43 return; 44 } 45 int m = (l + r) >> 1; 46 build(ls); 47 build(rs); 48 pushup(o); 49 } 50 51 inline void pushdown(int o, int m) 52 { 53 if(add[o] == 0 && mul[o] == 1) return; 54 c[o << 1] = (c[o << 1] * mul[o] + add[o] * (m - (m >> 1))) % P; 55 c[o << 1 | 1] = (c[o << 1 | 1] * mul[o] + add[o] * (m >> 1)) % P; 56 add[o << 1] = (add[o << 1] * mul[o] + add[o]) % P; 57 add[o << 1 | 1] = (add[o << 1 | 1] * mul[o] + add[o]) % P; 58 mul[o << 1] = (mul[o << 1] * mul[o]) % P; 59 mul[o << 1 | 1] = (mul[o << 1 | 1] * mul[o]) % P; 60 add[o] = 0; 61 mul[o] = 1; 62 } 63 64 inline void update(int f, int d, int o, int l, int r) 65 { 66 if(L <= l && r <= R) 67 { 68 if(f == 2) 69 { 70 add[o] = (add[o] + d) % P; 71 c[o] = (c[o] + d * (r - l + 1)) % P; 72 } 73 else 74 { 75 mul[o] = (mul[o] * d) % P; 76 add[o] = (add[o] * d) % P; 77 c[o] = (c[o] * d) % P; 78 } 79 return; 80 } 81 pushdown(o, r - l + 1); 82 int m = (l + r) >> 1; 83 if(L <= m) update(f, d, ls); 84 if(m < R) update(f, d, rs); 85 pushup(o); 86 } 87 88 inline long long query(int o, int l, int r) 89 { 90 if(L <= l && r <= R) return c[o]; 91 pushdown(o, r - l + 1); 92 int m = (l + r) >> 1; 93 long long ret = 0; 94 if(L <= m) ret += query(ls); 95 if(m < R) ret += query(rs); 96 return ret; 97 } 98 99 int main() 100 { 101 int N, Q; 102 N = read(); 103 P = read(); 104 build(root); 105 Q = read(); 106 while(Q--) 107 { 108 int a, x, y, k; 109 a = read(); 110 if(a == 1 || a == 2) 111 { 112 x = read(); 113 y = read(); 114 k = read(); 115 L = x; 116 R = y; 117 update(a, k, root); 118 } 119 else 120 { 121 x = read(); 122 y = read(); 123 L = x; 124 R = y; 125 printf("%lld\n", query(root) % P); 126 } 127 } 128 return 0; 129 }
Trie树
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 #define idx(x) x - ‘a‘ 8 const int maxn = 10000; 9 struct trie 10 { 11 int next[26];//next数组中存放的下标表示他的子树在tree[]中的位置 12 int val;//表示是否存在当前字符串 13 }tree[maxn]; 14 15 int nex, T;//nex表示tree中下标 16 char str[maxn]; 17 18 void Insert(char *s)//插入 19 { 20 int i, rt = 0, len = strlen(s) - 1; 21 for(i = 0; i <= len; i++) 22 { 23 int c = idx(s[i]); 24 if(!tree[rt].next[c]) tree[rt].next[c] = ++nex; 25 rt = tree[rt].next[c];//迭代 26 } 27 tree[rt].val = 1; 28 } 29 30 bool Find(char *s)//查找 31 { 32 int i, rt = 0, len = strlen(s) - 1; 33 for(i = 0; i <= len; i++) 34 { 35 int c = idx(s[i]); 36 if(!tree[rt].next[c]) return 0; 37 rt = tree[rt].next[c];//迭代 38 } 39 if(tree[rt].val) return 1; 40 return 0; 41 } 42 43 int main() 44 { 45 scanf("%d", &T); 46 while(T--) 47 { 48 scanf("%s", str); 49 Insert(str); 50 } 51 while(scanf("%s", str)) 52 if(Find(str)) printf("Yes\n"); 53 else printf("No\n"); 54 return 0; 55 }
KMP
1 #include <cstdio> 2 #include <cstring> 3 4 int lenT, lenP; 5 int next[1001]; 6 char T[1000001], P[1001]; 7 8 void make_next() 9 { 10 int i, k = 0; 11 for(i = 1; i < lenP; i++) 12 { 13 while(k && P[i] != P[k]) k = next[k - 1]; 14 if(P[i] == P[k]) k++; 15 next[i] = k; 16 } 17 } 18 19 int kmp() 20 { 21 int i, k = 0; 22 make_next(); 23 for(i = 0; i < lenT; i++) 24 { 25 while(k && P[k] != T[i]) k = next[k - 1]; 26 if(P[k] == T[i]) k++; 27 if(k == lenP) printf("%d\n", i - lenP + 2); 28 } 29 } 30 31 int main() 32 { 33 int i; 34 scanf("%s", T); 35 scanf("%s", P); 36 lenT = strlen(T); 37 lenP = strlen(P); 38 kmp(); 39 for(i = 0; i < lenP; i++) printf("%d ", next[i]); 40 return 0; 41 }
spfa+链式前向星
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 8 int n, p, c, ans, cnt; 9 long long m; 10 struct node 11 { 12 int to, next; 13 }edge[5000001]; 14 int dis[5000001], head[500001], x, y; 15 bool vis[5000001]; 16 17 void spfa() 18 { 19 int i, j; 20 queue <int> q; 21 memset(dis, 0x7f, sizeof(dis)); 22 dis[c] = 1; 23 vis[c] = 1; 24 q.push(c); 25 while(!q.empty()) 26 { 27 i = q.front(); 28 q.pop(); 29 vis[i] = 0; 30 for(j = head[i]; j >= 0; j = edge[j].next) 31 if(dis[edge[j].to] > dis[i] + 1) 32 { 33 dis[edge[j].to] = dis[i] + 1; 34 if(!vis[edge[j].to]) 35 { 36 q.push(edge[j].to); 37 vis[edge[j].to] = 1; 38 } 39 } 40 } 41 } 42 43 int main() 44 { 45 int i, j; 46 scanf("%d %d %d", &n, &p, &c); 47 scanf("%d", &m); 48 memset(head, -1, sizeof(head)); 49 for(i = 1; i <= p; i++) 50 { 51 scanf("%d %d", &x, &y); 52 edge[cnt].to = y; 53 edge[cnt].next = head[x]; 54 head[x] = cnt++; 55 edge[cnt].to = x; 56 edge[cnt].next = head[y]; 57 head[y] = cnt++; 58 } 59 spfa(); 60 for(i = 1; i <= n; i++) ans = max(ans, dis[i]); 61 printf("%lld", ans + m); 62 return 0; 63 } 64 复制代码
tarjan求强连通分量
1 #include <cstdio> 2 #include <cstring> 3 #include <stack> 4 5 int n, m, cnt, index, ans, size; 6 int head[10001], to[10001], next[10001], dfn[10001], low[10001], belong[10001]; 7 bool ins[10001]; 8 std::stack <int> s; 9 10 inline void add(int x, int y) 11 { 12 to[cnt] = y; 13 next[cnt] = head[x]; 14 head[x] = cnt++; 15 } 16 17 void tarjan(int u) 18 { 19 dfn[u] = low[u] = ++index; 20 s.push(u); 21 ins[u] = 1; 22 int i, v; 23 for(i = head[u]; i != -1; i = next[i]) 24 { 25 v = to[i]; 26 if(!dfn[v]) 27 { 28 tarjan(v); 29 low[u] = std::min(low[u], low[v]); 30 } 31 else if(ins[v]) low[u] = std::min(low[u], dfn[v]); 32 } 33 if(low[u] == dfn[u]) 34 { 35 size++; 36 do 37 { 38 v = s.top(); 39 ins[v] = 0; 40 belong[v] = size; 41 s.pop(); 42 }while(v != u); 43 } 44 } 45 46 int main() 47 { 48 int i, x, y; 49 scanf("%d %d", &n, &m); 50 memset(head, -1, sizeof(head)); 51 for(i = 1; i <= m; i++) 52 { 53 scanf("%d %d", &x, &y); 54 add(x, y); 55 } 56 for(i = 1; i <= n; i++)//有可能是非连通图 57 if(!dfn[i]) 58 tarjan(i); 59 printf("%d\n", size); 60 for(i = 1; i <= n; i++) printf("%d ", belong[i]); 61 return 0; 62 }
tarjan求lca
1 #include <cstdio> 2 #include <cstring> 3 4 const int maxn = 500001; 5 6 int n, m, cnt, s, cns; 7 int x, y, z[maxn];//z是x和y的lca 8 int f[maxn], head[maxn], from[maxn]; 9 bool vis[maxn]; 10 struct node 11 { 12 int to, next; 13 }e[2 * maxn]; 14 struct Node 15 { 16 int to, next, num; 17 }q[2 * maxn]; 18 19 inline int read()//读入优化 20 { 21 int x = 0, f = 1; 22 char ch = getchar(); 23 while(ch < ‘0‘ || ch > ‘9‘) 24 { 25 if(ch == ‘-‘) f = -1; 26 ch = getchar(); 27 } 28 while(ch >= ‘0‘ && ch <= ‘9‘) 29 { 30 x = x * 10 + ch - ‘0‘; 31 ch = getchar(); 32 } 33 return x * f; 34 } 35 36 inline void ask(int u, int v, int i)//储存待询问的结构体,也是链式前向星优化 37 { 38 q[cns].num = i;//num表示第几次询问 39 q[cns].to = v; 40 q[cns].next = from[u]; 41 from[u] = cns++; 42 } 43 44 inline void add(int u, int v)// 45 { 46 e[cnt].to = v; 47 e[cnt].next = head[u]; 48 head[u] = cnt++; 49 } 50 51 inline int find(int a) 52 { 53 return a == f[a] ? a : f[a] = find(f[a]);//路径压缩优化 54 } 55 56 /*inline void Union(int a, int b) 57 { 58 int fx = find(a), fy = find(b); 59 if(fx == fy) return; 60 f[fy] = fx; 61 }*/ 62 63 inline void tarjan(int k) 64 { 65 int i, j; 66 vis[k] = 1; 67 f[k] = k; 68 for(i = head[k]; i != -1; i = e[i].next) 69 if(!vis[e[i].to]) 70 { 71 tarjan(e[i].to); 72 //Union(k, e[i].to); 73 f[e[i].to] = k; 74 } 75 for(i = from[k]; i != -1; i = q[i].next) 76 if(vis[q[i].to] == 1) 77 z[q[i].num] = find(q[i].to); 78 } 79 80 int main() 81 { 82 int i, j, u, v; 83 n = read(); 84 m = read(); 85 s = read(); 86 memset(head, -1, sizeof(head)); 87 memset(from, -1, sizeof(from)); 88 for(i = 1; i <= n - 1; i++) 89 { 90 u = read(); 91 v = read(); 92 add(u, v);//注意添加两遍 93 add(v, u); 94 } 95 for(i = 1; i <= m; i++) 96 { 97 x = read(); 98 y = read(); 99 ask(x, y, i);//两遍 100 ask(y, x, i); 101 } 102 tarjan(s); 103 for(i = 1; i <= m; i++) printf("%d\n", z[i]); 104 return 0; 105 }
倍增求lca
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 5 const int maxn = 500001; 6 int n, m, cnt, s; 7 int next[2 * maxn], to[2 * maxn], head[2 * maxn], deep[maxn], p[maxn][21]; 8 9 inline void add(int x, int y) 10 { 11 to[cnt] = y; 12 next[cnt] = head[x]; 13 head[x] = cnt++; 14 } 15 16 inline void dfs(int i) 17 { 18 int j; 19 for(j = head[i]; j != -1; j = next[j]) 20 if(!deep[to[j]]) 21 { 22 deep[to[j]] = deep[i] + 1; 23 p[to[j]][0] = i; 24 dfs(to[j]); 25 } 26 } 27 28 inline void init() 29 { 30 int i, j; 31 for(j = 1; (1 << j) <= n; j++) 32 for(i = 1; i <= n; i++) 33 p[i][j] = p[p[i][j - 1]][j - 1]; 34 } 35 36 inline int lca(int a, int b) 37 { 38 int i, j; 39 if(deep[a] < deep[b]) std::swap(a, b); 40 for(i = 0; (1 << i) <= deep[a]; i++); 41 i--; 42 for(j = i; j >= 0; j--) 43 if(deep[a] - (1 << j) >= deep[b]) 44 a = p[a][j]; 45 if(a == b) return a; 46 for(j = i; j >= 0; j--) 47 if(p[a][j] != p[b][j]) 48 { 49 a = p[a][j]; 50 b = p[b][j]; 51 } 52 return p[a][0]; 53 } 54 55 int main() 56 { 57 int i, j, x, y; 58 memset(head, -1, sizeof(head)); 59 scanf("%d %d %d", &n, &m, &s); 60 for(i = 1; i <= n - 1; i++) 61 { 62 scanf("%d %d", &x, &y); 63 add(x, y); 64 add(y, x); 65 } 66 deep[s] = 1; 67 dfs(s); 68 init(); 69 for(i = 1; i <= m; i++) 70 { 71 scanf("%d %d", &x, &y); 72 printf("%d\n", lca(x, y)); 73 } 74 return 0; 75 }
tarjan求割边割点
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int maxn = 100001; 10 int n, m, cnt, rp; 11 int next[2 * maxn], to[2 * maxn], head[maxn], low[maxn], dfn[maxn], father[maxn];//father为父节点 12 vector <int> cut_point; 13 vector < pair <int, int> > cut_edge; 14 15 void add(int x, int y) 16 { 17 to[cnt] = y; 18 next[cnt] = head[x]; 19 head[x] = cnt++; 20 } 21 22 void tarjan(int u) 23 { 24 int i, v, child = 0;//child表示当前节点孩子数量 25 bool flag = 0; 26 dfn[u] = low[u] = ++rp; 27 for(i = head[u]; i != -1; i = next[i]) 28 { 29 v = to[i]; 30 if(!dfn[v]) 31 { 32 child++; 33 father[v] = u; 34 tarjan(v); 35 if(low[v] >= dfn[u]) flag = 1;//割点 36 if(low[v] > dfn[u]) cut_edge.push_back(make_pair(min(u, v), max(u, v)));//割边 37 low[u] = min(low[u], low[v]); 38 } 39 else if(v != father[u]) low[u] = min(low[u], dfn[v]); 40 41 } 42 //根节点若有两棵或两棵以上的子树则该为割点 43 //非根节点若所有子树节点均没有指向u的祖先节点的回边则为割点 44 if((father[u] == 0 && child > 1) || (father[u] && flag)) cut_point.push_back(u); 45 } 46 47 int main() 48 { 49 int i, j, x, y, s; 50 memset(head, -1, sizeof(head)); 51 scanf("%d %d", &n, &m); 52 for(i = 1; i <= m; i++) 53 { 54 scanf("%d %d", &x, &y); 55 add(x, y); 56 add(y, x); 57 } 58 for(i = 1; i <= n; i++)//图可能不联通(mdzz的洛谷模板题) 59 if(!dfn[i]) 60 tarjan(i); 61 sort(cut_point.begin(), cut_point.end()); 62 s = cut_point.size(); 63 printf("%d\n", s); 64 for(i = 0; i < s; i++) printf("%d ", cut_point[i]);//输出割点 65 s = cut_edge.size(); 66 printf("\n%d\n", s); 67 for(i = 0; i < s; i++) printf("%d %d\n", cut_edge[i].first, cut_edge[i].second);//输出割边 68 return 0; 69 }
最大流dinic
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 using namespace std; 6 7 int n, m, cnt, ans; 8 int head[10001], to[200001], val[200001], next[200001], dis[10001], cur[10001]; 9 10 void add(int x, int y, int z) 11 { 12 to[cnt] = y; 13 val[cnt] += z; 14 next[cnt] = head[x]; 15 head[x] = cnt++; 16 } 17 18 int dfs(int u, int t, int maxflow) 19 { 20 if(u == t) return maxflow; 21 int i, ret = 0, d, v; 22 for(i = cur[u]; i != -1; i = next[i]) 23 { 24 v = to[i]; 25 if(val[i] && dis[v] == dis[u] + 1) 26 { 27 d = dfs(v, t, min(val[i], maxflow - ret)); 28 ret += d; 29 val[i] -= d; 30 val[i ^ 1] += d; 31 cur[u] = i; 32 if(ret == maxflow) return ret; 33 } 34 } 35 return ret; 36 } 37 38 bool bfs(int s, int t) 39 { 40 queue <int> q; 41 memset(dis, -1, sizeof(dis)); 42 q.push(s); 43 dis[s] = 0; 44 int i, u, v; 45 while(!q.empty()) 46 { 47 u = q.front(); 48 q.pop(); 49 for(i = head[u]; i != -1; i = next[i]) 50 { 51 v = to[i]; 52 if(val[i] && dis[v] == -1) 53 { 54 dis[v] = dis[u] + 1; 55 if(v == t) return 1; 56 q.push(v); 57 } 58 } 59 } 60 return 0; 61 } 62 63 int main() 64 { 65 int i, j, s, t, x, y, z; 66 scanf("%d %d %d %d", &n, &m, &s, &t); 67 memset(head, -1, sizeof(head)); 68 for(i = 1; i <= m; i++) 69 { 70 scanf("%d %d %d", &x, &y, &z); 71 add(x, y, z); 72 add(y, x, 0); 73 } 74 //dinic 75 while(bfs(s, t)) 76 { 77 for(i = 1; i <= n; i++) cur[i] = head[i]; 78 ans += dfs(s, t, 1e9); 79 } 80 printf("%d", ans); 81 return 0; 82 } 83 84 Dinic
最小费用最大流Dinic+spfa
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 using namespace std; 6 7 const int INF = 1 << 26; 8 int n, m, s, t, cnt; 9 int to[100001], val[100001], next[100001], cost[100001], head[5001], pre[5001], path[5001], dis[5001]; 10 //pre记录前一个节点,path记录边 11 bool vis[5001]; 12 13 inline int read()//读入优化 14 { 15 int x = 0, f = 1; 16 char ch = getchar(); 17 while(ch < ‘0‘ || ch > ‘9‘) 18 { 19 if(ch == ‘-‘) f = -1; 20 ch = getchar(); 21 } 22 while(ch >= ‘0‘ && ch <= ‘9‘) 23 { 24 x = x * 10 + ch - ‘0‘; 25 ch = getchar(); 26 } 27 return x * f; 28 } 29 30 inline void add(int a, int b, int c, int d) 31 { 32 to[cnt] = b; 33 val[cnt] = c; 34 cost[cnt] = d; 35 next[cnt] = head[a]; 36 head[a] = cnt++; 37 } 38 39 inline bool spfa() 40 { 41 int u, i, v; 42 memset(vis, 0, sizeof(vis)); 43 memset(pre, -1, sizeof(pre)); 44 for(i = 1; i <= n; i++) dis[i] = INF; 45 dis[s] = 0; 46 queue <int> q; 47 q.push(s); 48 vis[s] = 1; 49 while(!q.empty()) 50 { 51 u = q.front(); 52 vis[u] = 0; 53 q.pop(); 54 for(i = head[u]; i != -1; i = next[i]) 55 { 56 v = to[i]; 57 if(val[i] > 0 && dis[v] > dis[u] + cost[i]) 58 { 59 dis[v] = dis[u] + cost[i]; 60 pre[v] = u; 61 path[v] = i; 62 if(!vis[v]) 63 { 64 q.push(v); 65 vis[v] = 1; 66 } 67 } 68 } 69 } 70 return pre[t] != -1; 71 } 72 73 int main() 74 { 75 int i, j, a, b, c, d, money = 0, flow = 0, f; 76 n = read(); 77 m = read(); 78 s = read(); 79 t = read(); 80 memset(head, -1, sizeof(head)); 81 for(i = 1; i <= m; i++) 82 { 83 a = read(); 84 b = read(); 85 c = read(); 86 d = read(); 87 add(a, b, c, d); 88 add(b, a, 0, -d); 89 } 90 while(spfa()) 91 { 92 f = INF; 93 for(i = t; i != s; i = pre[i]) f = min(f, val[path[i]]); 94 flow += f; 95 money += dis[t] * f; 96 for(i = t; i != s; i = pre[i]) 97 { 98 val[path[i]] -= f; 99 val[path[i] ^ 1] += f; 100 } 101 } 102 printf("%d %d", flow, money); 103 return 0; 104 }
最小费用最大流Dinic+heap优化Dijkstra
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #define Heap pair<int, int> 5 6 using namespace std; 7 8 const int INF = 1 << 26; 9 int n, m, s, t, cnt; 10 int to[100001], val[100001], next[100001], cost[100001], head[5001], pre[5001], path[5001], dis[5001]; 11 //pre记录前一个节点,path记录边 12 13 inline int read()//读入优化 14 { 15 int x = 0, f = 1; 16 char ch = getchar(); 17 while(ch < ‘0‘ || ch > ‘9‘) 18 { 19 if(ch == ‘-‘) f = -1; 20 ch = getchar(); 21 } 22 while(ch >= ‘0‘ && ch <= ‘9‘) 23 { 24 x = x * 10 + ch - ‘0‘; 25 ch = getchar(); 26 } 27 return x * f; 28 } 29 30 inline void add(int a, int b, int c, int d) 31 { 32 to[cnt] = b; 33 val[cnt] = c; 34 cost[cnt] = d; 35 next[cnt] = head[a]; 36 head[a] = cnt++; 37 } 38 39 bool Dijkstra() 40 { 41 int i, v; 42 Heap u; 43 memset(pre, -1, sizeof(pre)); 44 for(i = 1; i <= n; i++) dis[i] = INF; 45 dis[s] = 0; 46 priority_queue <Heap, vector <Heap>, greater <Heap> > q; 47 q.push(make_pair(0, s));//前一个表示当前节点到起点的距离,后一个是当前节点 48 while(!q.empty()) 49 { 50 u = q.top(); 51 q.pop(); 52 if(u.first != dis[u.second]) continue; 53 for(i = head[u.second]; i != -1; i = next[i]) 54 { 55 v = to[i]; 56 if(val[i] > 0 && dis[v] > dis[u.second] + cost[i]) 57 { 58 dis[v] = dis[u.second] + cost[i]; 59 pre[v] = u.second; 60 path[v] = i; 61 q.push(make_pair(dis[v], v)); 62 } 63 } 64 } 65 return pre[t] != -1; 66 } 67 68 int main() 69 { 70 int i, j, a, b, c, d, money = 0, flow = 0, f; 71 n = read(); 72 m = read(); 73 s = read(); 74 t = read(); 75 memset(head, -1, sizeof(head)); 76 for(i = 1; i <= m; i++) 77 { 78 a = read(); 79 b = read(); 80 c = read(); 81 d = read(); 82 add(a, b, c, d); 83 add(b, a, 0, -d); 84 } 85 while(Dijkstra()) 86 { 87 f = INF; 88 for(i = t; i != s; i = pre[i]) f = min(f, val[path[i]]); 89 flow += f; 90 money += dis[t] * f; 91 for(i = t; i != s; i = pre[i]) 92 { 93 val[path[i]] -= f; 94 val[path[i] ^ 1] += f; 95 } 96 } 97 printf("%d %d", flow, money); 98 return 0; 99 }
heap优化Dijkstra
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #define Heap pair<int, int> 5 //第一个int存的是到起点的距离,第二个int存的是点的编号 6 7 using namespace std; 8 9 const int INF = 2147483647; 10 int n, m, t, cnt; 11 int next[1000001], to[1000001], val[1000001], head[10001], dis[10001]; 12 bool vis[1000001]; 13 priority_queue <Heap, vector <Heap>, greater <Heap> > q; 14 //按照第一个int从小到大排序 15 16 inline void add(int a, int b, int c) 17 { 18 to[cnt] = b; 19 val[cnt] = c; 20 next[cnt] = head[a]; 21 head[a] = cnt++; 22 } 23 24 inline void Dijkstra(int s)//以s为起点 25 { 26 int i, u, v; 27 Heap x; 28 for(i = 1; i <= n; i++) dis[i] = INF; 29 dis[s] = 0; 30 q.push(make_pair(0, s));//入队 31 while(!q.empty()) 32 { 33 x = q.top(); 34 q.pop(); 35 u = x.second; 36 if(vis[u]) continue;//判断是否已经是最短路径上的点 37 vis[u] = 1; 38 for(i = head[u]; i != -1; i = next[i])//更新距离 39 { 40 v = to[i]; 41 if(dis[v] > dis[u] + val[i]) 42 { 43 dis[v] = dis[u] + val[i]; 44 q.push(make_pair(dis[v], v)); 45 } 46 } 47 } 48 } 49 50 int main() 51 { 52 int i, j, a, b, c, s; 53 scanf("%d %d %d", &n, &m, &s); 54 memset(head, -1, sizeof(head)); 55 for(i = 1; i <= m; i++) 56 { 57 scanf("%d %d %d", &a, &b, &c); 58 add(a, b, c); 59 } 60 Dijkstra(s); 61 for(i = 1; i <= n; i++) printf("%d ", dis[i]); 62 return 0; 63 }
二分图染色
1 #include <cstdio> 2 #include <cstring> 3 4 int n, e, cnt; 5 int head[1001], next[1001], to[1001], color[1001]; 6 7 void add(int x, int y) 8 { 9 to[cnt] = y; 10 next[cnt] = head[x]; 11 head[x] = cnt++; 12 } 13 14 bool dfs(int u, int c) 15 { 16 int i, v; 17 color[u] = c; 18 for(i = head[u]; i != -1; i = next[i]) 19 { 20 v = to[i]; 21 if(color[v] == c) return 0; 22 if(color[v] == 0 && !dfs(v, -c)) return 0; 23 } 24 return 1; 25 } 26 27 bool solve() 28 { 29 int i; 30 for(i = 1; i <= n; i++) 31 if(color[i] == 0 && !dfs(i, 1)) 32 return 0; 33 return 1; 34 } 35 36 int main() 37 { 38 int i, x, y; 39 scanf("%d %d", &n, &e); 40 memset(head, -1, sizeof(head)); 41 for(i = 1; i <= e; i++) 42 { 43 scanf("%d %d", &x, &y); 44 add(x, y); 45 add(y, x); 46 } 47 if(solve()) printf("YES"); 48 else printf("NO"); 49 return 0; 50 }
二分图匹配匈牙利算法
1 #include <cstdio> 2 #include <cstring> 3 4 using namespace std; 5 6 int n, m, k, cp, cnt; 7 int girl[10010], to[1000001], next[1000001], head[10010]; 8 //girl[i]记录第i个girl所属的boy 9 bool dog[10010]; 10 //dog[i]记录i是否脱单 11 12 inline void add(int x, int y) 13 { 14 to[cnt] = y; 15 next[cnt] = head[x]; 16 head[x] = cnt++; 17 } 18 19 int find_girl(int u) 20 { 21 int i, v; 22 for(i = head[u]; i != -1; i = next[i])//找所有有好感的对象 23 { 24 v = to[i]; 25 if(!dog[v]) 26 //单身(这里标记的意思是这次查找曾试图改变过该妹子的归属问题,但是没有成功,所以就不用瞎费工夫了) 27 { 28 dog[v] = 1; 29 if(!girl[v] || find_girl(girl[v]))//名花无主或者能腾出个位置来 30 { 31 girl[v] = u; 32 return 1; 33 } 34 } 35 } 36 return 0; 37 } 38 39 int main() 40 { 41 int i, j, x, y; 42 scanf("%d %d %d", &n, &m, &k); 43 memset(head, -1, sizeof(head)); 44 for(i = 1; i <= k; i++) 45 { 46 scanf("%d %d", &x, &y); 47 if(x > n || y > m) continue; 48 add(x, y);//有暧昧关系 49 } 50 for(i = 1; i <= n; i++) 51 { 52 memset(dog, 0, sizeof(dog)); 53 if(find_girl(i)) cp++; 54 } 55 printf("%d", cp); 56 return 0; 57 }
二分图匹配dinic
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 using namespace std; 6 7 int n, m, cnt, ans, e; 8 int head[100001], to[2000001], val[2000001], next[2000001], dis[100001], cur[100001]; 9 10 void add(int x, int y, int z) 11 { 12 to[cnt] = y; 13 val[cnt] += z; 14 next[cnt] = head[x]; 15 head[x] = cnt++; 16 } 17 18 int dfs(int u, int t, int maxflow) 19 { 20 if(u == t) return maxflow; 21 int i, ret = 0, d, v; 22 for(i = cur[u]; i != -1; i = next[i]) 23 { 24 v = to[i]; 25 if(val[i] && dis[v] == dis[u] + 1) 26 { 27 d = dfs(v, t, min(val[i], maxflow - ret)); 28 ret += d; 29 val[i] -= d; 30 val[i ^ 1] += d; 31 cur[u] = i; 32 if(ret == maxflow) return ret; 33 } 34 } 35 return ret; 36 } 37 38 bool bfs(int s, int t) 39 { 40 queue <int> q; 41 memset(dis, -1, sizeof(dis)); 42 q.push(s); 43 dis[s] = 0; 44 int i, u, v; 45 while(!q.empty()) 46 { 47 u = q.front(); 48 q.pop(); 49 for(i = head[u]; i != -1; i = next[i]) 50 { 51 v = to[i]; 52 if(val[i] && dis[v] == -1) 53 { 54 dis[v] = dis[u] + 1; 55 if(v == t) return 1; 56 q.push(v); 57 } 58 } 59 } 60 return 0; 61 } 62 63 int main() 64 { 65 int i, j, s, t, x, y; 66 scanf("%d %d %d", &n, &m, &e); 67 memset(head, -1, sizeof(head)); 68 s = 0; 69 t = n + m + 2; 70 for(i = 1; i <= n; i++) 71 { 72 add(s, i, 1); 73 add(i, s, 0); 74 } 75 for(i = 1; i <= m; i++) 76 { 77 add(n + i, t, 1); 78 add(t, n + i, 0); 79 } 80 for(i = 1; i <= e; i++) 81 { 82 scanf("%d %d", &x, &y); 83 if(x > n || y > m) continue; 84 add(x, y + n, 1); 85 add(y + n, x, 0); 86 } 87 while(bfs(s, t)) 88 { 89 for(i = 0; i <= n + m + 2; i++) cur[i] = head[i]; 90 ans += dfs(s, t, 1e9); 91 } 92 printf("%d", ans); 93 return 0; 94 }
二分图最大权完美匹配 KM算法
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 7 const int INF = 0x3f3f3f3f; 8 const int maxn = 301; 9 int n; 10 int love[maxn][maxn]; 11 //男女好感度 12 int ex_boy[maxn], ex_girl[maxn]; 13 //每个男生期望值,每个妹子期望值 14 int match[maxn]; 15 //记录每个男生匹配到的妹子 如果没有则为-1 16 int slack[maxn]; 17 //记录每个男生如果能被妹子倾心最少还需要多少期望值 18 bool vis_boy[maxn], vis_girl[maxn]; 19 //记录每一轮匹配匹配到的男生和女生 20 21 bool find(int i) 22 { 23 int j, gap; 24 vis_girl[i] = 1; 25 for(j = 1; j <= n; j++) 26 { 27 if(vis_boy[j]) continue; //每一轮匹配,每个男生只尝试一次 28 gap = ex_girl[i] + ex_boy[j] - love[i][j]; 29 if(gap == 0) //如果符合要求 30 { 31 vis_boy[j] = 1; 32 //如果没有找到匹配男生,或者该男生的妹子可以找到其他人 33 if(match[j] == -1 || find(match[j])) 34 { 35 match[j] = i; 36 return 1; 37 } 38 } 39 else slack[j] = min(slack[j], gap); 40 // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 备胎的样子【捂脸 41 } 42 return 0; 43 } 44 45 int KM() 46 { 47 int i, j, d, ret = 0; 48 memset(match, -1, sizeof(match)); 49 //初始每个男生都没有匹配到女生 50 memset(ex_boy, 0, sizeof(ex_boy)); 51 //初始每个男生期望值为0 52 53 //每个女生的期望值是与她相连的男生的最大好感度 54 for(i = 1; i <= n; i++) 55 for(j = 1; j <= n; j++) 56 ex_girl[i] = max(ex_girl[i], love[i][j]); 57 58 //尝试为每个女生解决归宿问题 59 for(i = 1; i <= n; i++) 60 { 61 memset(slack, 127 / 3, sizeof(slack)); 62 //因为要取小值,初始化无穷大 63 while(1) 64 { 65 //为每个女生解决归宿的方法是: 66 //如果找不到就降低期望值,直到找到为止 67 68 //记录每轮匹配中男女是否被匹配过 69 memset(vis_girl, 0, sizeof(vis_girl)); 70 memset(vis_boy, 0, sizeof(vis_boy)); 71 72 //找到归宿,退出 73 if(find(i)) break; 74 75 //如果找不到,就降低期望值 76 //最小可降低的期望值 77 d = INF; 78 for(j = 1; j <= n; j++) 79 if(!vis_boy[j]) 80 d = min(d, slack[j]); 81 82 for(j = 1; j <= n; j++) 83 { 84 //所有访问过的妹子降低期望值 85 if(vis_girl[j]) ex_girl[j] -= d; 86 //所有访问过的男生增加期望值 87 if(vis_boy[j]) ex_boy[j] += d; 88 } 89 } 90 } 91 92 //匹配完成,找出所有匹配的好感度之和 93 for(i = 1; i <= n; i++) ret += love[match[i]][i]; 94 return ret; 95 } 96 97 int main() 98 { 99 int i, j; 100 while(~scanf("%d", &n)) 101 { 102 for(i = 1; i <= n; i++) 103 for(j = 1; j <= n; j++) 104 scanf("%d", &love[i][j]); 105 printf("%d\n", KM()); 106 } 107 return 0; 108 }
树链剖分
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define rt 1, 1, n 5 #define ls o << 1, l, m 6 #define rs o << 1 | 1, m + 1, r 7 8 using namespace std; 9 10 const int maxn = 300001; 11 const int INF = 99999999; 12 int n, m, q, cnt, tim; 13 int a[maxn], head[maxn], to[maxn << 2], next[maxn << 2], deep[maxn], size[maxn]; 14 int son[maxn], top[maxn], f[maxn], tid[maxn], rank[maxn], sumv[maxn], maxv[maxn]; 15 //a节点权值, deep节点深度, size以x为根的子树节点个数, son重儿子, top当前节点所在链的顶端节点 16 //f当前节点父亲, tid保存树中每个节点剖分后的新编号, rank保存剖分后的节点在线段树中的位置 17 18 void add(int x, int y) 19 { 20 to[cnt] = y; 21 next[cnt] = head[x]; 22 head[x] = cnt++; 23 } 24 25 void dfs1(int u, int father)//记录所有重边 26 { 27 int i, v; 28 f[u] = father; 29 size[u] = 1; 30 deep[u] = deep[father] + 1; 31 for(i = head[u]; i != -1; i = next[i]) 32 { 33 v = to[i]; 34 if(v == father) continue; 35 dfs1(v, u); 36 size[u] += size[v]; 37 if(son[u] == -1 || size[v] > size[son[u]]) son[u] = v; 38 } 39 } 40 41 void dfs2(int u, int tp) 42 { 43 int i, v; 44 top[u] = tp; 45 tid[u] = ++tim; 46 rank[tim] = u; 47 if(son[u] == -1) return; 48 dfs2(son[u], tp);//重边 49 for(i = head[u]; i != -1; i = next[i]) 50 { 51 v = to[i]; 52 if(v != son[u] && v != f[u]) dfs2(v, v);//轻边 53 } 54 } 55 56 void pushup(int o) 57 { 58 sumv[o] = sumv[o << 1] + sumv[o << 1 | 1]; 59 maxv[o] = max(maxv[o << 1], maxv[o << 1 | 1]); 60 } 61 62 void updata(int o, int l, int r, int d, int x) 63 { 64 int m = (l + r) >> 1; 65 if(l == r) 66 { 67 sumv[o] = maxv[o] = x; 68 return; 69 } 70 if(d <= m) updata(ls, d, x); 71 else updata(rs, d, x); 72 pushup(o); 73 } 74 75 void build(int o, int l, int r) 76 { 77 int m = (l + r) >> 1; 78 if(l == r) 79 { 80 sumv[o] = maxv[o] = a[rank[l]]; 81 return; 82 } 83 build(ls); 84 build(rs); 85 pushup(o); 86 } 87 88 int querymax(int o, int l, int r, int ql, int qr) 89 { 90 int m = (l + r) >> 1, ans = -INF; 91 if(ql <= l && r <= qr) return maxv[o]; 92 if(ql <= m) ans = max(ans, querymax(ls, ql, qr)); 93 if(m < qr) ans = max(ans, querymax(rs, ql, qr)); 94 pushup(o); 95 return ans; 96 } 97 98 int qmax(int u, int v) 99 { 100 int ans = -INF; 101 while(top[u] != top[v])//判断是否在一条重链上 102 { 103 if(deep[top[u]] < deep[top[v]]) swap(u, v);//深度不同,先处理深度大的 104 ans = max(ans, querymax(rt, tid[top[u]], tid[u])); 105 u = f[top[u]]; 106 } 107 if(deep[u] < deep[v]) swap(u, v);//在同一条重链上了 108 ans = max(ans, querymax(rt, tid[v], tid[u])); 109 return ans; 110 } 111 112 int querysum(int o, int l, int r, int ql, int qr) 113 { 114 int m = (l + r) >> 1, ans = 0; 115 if(ql <= l && r <= qr) return sumv[o]; 116 if(ql <= m) ans += querysum(ls, ql, qr); 117 if(m < qr) ans += querysum(rs, ql, qr); 118 pushup(o); 119 return ans; 120 } 121 122 int qsum(int u, int v) 123 { 124 int ans = 0; 125 while(top[u] != top[v])//判断是否在一条重链上 126 { 127 if(deep[top[u]] < deep[top[v]]) swap(u, v);//深度不同,先处理深度大的 128 ans += querysum(rt, tid[top[u]], tid[u]); 129 u = f[top[u]]; 130 } 131 if(deep[u] < deep[v]) swap(u, v);//在同一条重链上了 132 ans += querysum(rt, tid[v], tid[u]); 133 return ans; 134 } 135 136 int main() 137 { 138 int i, j, x, y; 139 char s[11]; 140 memset(head, -1, sizeof(head)); 141 memset(son, -1, sizeof(son)); 142 scanf("%d", &n); 143 for(i = 1; i < n; i++) 144 { 145 scanf("%d %d", &x, &y); 146 add(x, y); 147 add(y, x); 148 } 149 for(i = 1; i <= n; i++) scanf("%d", &a[i]); 150 dfs1(1, 1);//根节点和他的父亲 151 dfs2(1, 1);//根节点和链头结点 152 build(rt); 153 scanf("%d", &q); 154 for(i = 1; i <= q; i++) 155 { 156 scanf("%s %d %d", s, &x, &y); 157 if(s[1] == ‘H‘) updata(rt, tid[x], y);//把位置为x的点修改为y 158 if(s[1] == ‘M‘) printf("%d\n", qmax(x, y)); 159 if(s[1] == ‘S‘) printf("%d\n", qsum(x, y)); 160 } 161 return 0; 162 }
左偏树
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 int n, m; 7 int f[100001], l[100001], r[100001], w[100001], d[100001];//f[i]表示第i个数所在堆的堆顶 8 bool b[100001];//表示是否被删除 9 //合并以 x, y 为根的堆 10 inline int merge(int x, int y) 11 { 12 if(!x || !y) return x + y; 13 if(w[x] > w[y]) swap(x, y); 14 r[x] = merge(r[x], y); 15 f[r[x]] = x;//并查集 16 if(d[l[x]] < d[r[x]]) swap(l[x], r[x]); 17 d[x] = d[r[x]] + 1; 18 return x; 19 } 20 21 inline int pop(int x)//返回新合并的堆的堆顶 22 { 23 int lc = l[x], rc = r[x]; 24 f[lc] = lc; 25 f[rc] = rc; 26 l[x] = r[x] = d[x] = 0; 27 return merge(lc, rc); 28 } 29 30 inline int find(int x) 31 { 32 return x == f[x] ? x : f[x] = find(f[x]); 33 } 34 35 inline int top(int x)//第几个数所在堆的堆顶 36 { 37 return w[x]; 38 } 39 40 int main() 41 { 42 int i, j, x, y, c, fx, fy; 43 scanf("%d %d", &n, &m); 44 for(i = 1; i <= n; i++) scanf("%d", &w[i]), f[i] = i; 45 for(i = 1; i <= m; i++) 46 { 47 scanf("%d", &c); 48 if(c == 1) 49 { 50 scanf("%d %d", &x, &y); 51 if(b[x] || b[y]) continue;//如果有一个数被删除 52 fx = find(x); 53 fy = find(y); 54 if(fx == fy) continue;//在同一个堆中 55 merge(fx, fy);//合并 56 } 57 else 58 { 59 scanf("%d", &x); 60 if(b[x]) printf("-1\n"); 61 else 62 { 63 fx = find(x); 64 printf("%d\n", w[fx]); 65 b[fx] = 1; 66 f[fx] = pop(fx); 67 //因为有的节点指向当前堆的堆顶,所以也得更新删除的堆顶 68 } 69 } 70 } 71 return 0; 72 }
Treap
1 #include <cstdio> 2 #include <cstdlib> 3 4 using namespace std; 5 6 int n, root, ans, tot; 7 int son[100001][2], size[100001], w[100001], rnd[100001], g[100001]; 8 //son[k][0] 表示 k 的左儿子, son[k][1] 表示 k 的右儿子 9 //size[k] 表示以 k 为根的子树的节点数 10 //w[k] 表示节点 k 的权值 11 //rnd[k] 表示节点 k 的优先级 12 //g[k] 表示节点 k 的个数(有可能有重复的) 13 14 //通过旋转来维护小根堆 15 //因为通过旋转根是会变的,所以得加 & 16 // x 为 0 是 右旋,x 为 1 是 左旋 17 //但是不必知道是左旋还是右旋 18 //只知道旋转一个节点的孩子就相当于把这个孩子旋转到父亲上 19 void turn(int &k, int x) 20 { 21 //位运算优化 ^ 可表示兄弟节点, 使得左旋和右旋合并 22 int t = son[k][x]; 23 son[k][x] = son[t][x ^ 1]; 24 son[t][x ^ 1] = k; 25 size[t] = size[k]; 26 size[k] = size[son[k][0]] + size[son[k][1]] + g[k]; 27 k = t; 28 } 29 30 void insert(int &k, int x)//小根堆_插入 31 { 32 if(!k) 33 { 34 k = ++tot; 35 w[k] = x; 36 rnd[k] = rand(); 37 g[k] = size[k] = 1; 38 return; 39 } 40 size[k]++; 41 if(w[k] == x) g[k]++; 42 else if(w[k] < x) 43 { 44 insert(son[k][1], x); 45 if(rnd[son[k][1]] < rnd[k]) turn(k, 1); 46 } 47 else 48 { 49 insert(son[k][0], x); 50 if(rnd[son[k][0] < rnd[k]]) turn(k, 0); 51 } 52 } 53 54 //通过旋转使得被删除点转移得到叶子节点上或只有一个儿子 55 void del(int &k, int x)//删除 56 { 57 if(!k) return; 58 if(w[k] == x) 59 { 60 if(g[k] > 1) 61 { 62 g[k]--; 63 size[k]--; 64 return; 65 } 66 //只有一个儿子, 直接继承 67 if(son[k][0] * son[k][1] == 0) k = son[k][0] + son[k][1]; 68 //旋转后须满足小根堆性质,所以右旋,旋转后 k 值改变,继续删除 69 else if(rnd[son[k][0]] < rnd[son[k][1]]) turn(k, 0), del(k, x); 70 //反之左旋 71 else turn(k, 1), del(k, x); 72 } 73 else 74 { 75 size[k]--; 76 if(w[k] < x) del(son[k][1], x); 77 else del(son[k][0], x); 78 } 79 } 80 81 //查询x数的排名(若有多个相同的数,因输出最小的排名) 82 int get_rank(int k, int x) 83 { 84 if(!k) return 0; 85 if(w[k] == x) return size[son[k][0]] + 1; 86 else if(w[k] < x) return size[son[k][0]] + g[k] + get_rank(son[k][1], x); 87 else return get_rank(son[k][0], x); 88 } 89 90 //找第 x 大的数 91 int get_kth(int k, int x) 92 { 93 if(!k) return 0; 94 if(size[son[k][0]] >= x) return get_kth(son[k][0], x); 95 else if(size[son[k][0]] + g[k] < x) return get_kth(son[k][1], x - size[son[k][0]] - g[k]); 96 else return w[k]; 97 } 98 99 // x 的前驱是比 x 小的数中最大的那个 100 void get_pre(int k, int x) 101 { 102 if(!k) return; 103 if(w[k] >= x) get_pre(son[k][0], x); 104 else ans = k, get_pre(son[k][1], x); 105 } 106 107 // x 的后继是比 x 大的数中最小的那个 108 void get_suc(int k, int x) 109 { 110 if(!k) return; 111 if(w[k] <= x) get_suc(son[k][1], x); 112 else ans = k, get_suc(son[k][0], x); 113 } 114 115 int main() 116 { 117 int i, opt, x; 118 scanf("%d", &n); 119 for(i = 1; i <= n; i++) 120 { 121 scanf("%d %d", &opt, &x); 122 switch(opt) 123 { 124 case 1: insert(root, x); break; 125 case 2: del(root, x); break; 126 case 3: printf("%d\n", get_rank(root, x)); break; 127 case 4: printf("%d\n", get_kth(root, x)); break; 128 case 5: ans = 0; get_pre(root, x); printf("%d\n", w[ans]); break; 129 case 6: ans = 0; get_suc(root, x); printf("%d\n", w[ans]); break; 130 } 131 } 132 return 0; 133 }
splay
1 #include <cstdio> 2 3 using namespace std; 4 5 const int N = 300005; 6 int n, root, sz; 7 int w[N], cnt[N], size[N], son[N][2], f[N]; 8 9 void clear(int x) 10 { 11 son[x][0] = son[x][1] = f[x] = cnt[x] = w[x] = size[x] = 0; 12 } 13 14 int get(int x) 15 { 16 return son[f[x]][1] == x; 17 } 18 19 void update(int x) 20 { 21 size[x] = cnt[x] + size[son[x][0]] + size[son[x][1]]; 22 } 23 24 void rotate(int x) 25 { 26 int old = f[x], oldf = f[old], wh = get(x); 27 son[old][wh] = son[x][wh ^ 1]; 28 if(son[old][wh]) f[son[old][wh]] = old; 29 son[x][wh ^ 1] = old; 30 f[old] = x; 31 if(oldf) son[oldf][son[oldf][1] == old] = x; 32 f[x] = oldf; 33 update(old); 34 update(x); 35 } 36 37 void splay(int x) 38 { 39 for(int fa; fa = f[x]; rotate(x)) 40 if(f[fa]) 41 rotate((get(x) == get(fa)) ? fa : x); 42 root = x; 43 } 44 45 void insert(int x) 46 { 47 if(!root) 48 { 49 root = ++sz; 50 w[sz] = x; 51 cnt[sz] = size[sz] = 1; 52 return; 53 } 54 int now = root, fa = 0; 55 while(1) 56 { 57 if(w[now] == x) 58 { 59 cnt[now]++; 60 update(now); 61 splay(now); 62 break; 63 } 64 fa = now; 65 now = son[now][x > w[now]]; 66 if(!now) 67 { 68 sz++; 69 w[sz] = x; 70 f[sz] = fa; 71 cnt[sz] = size[sz] = 1; 72 son[fa][x > w[fa]] = sz; 73 update(fa); 74 splay(sz); 75 break; 76 } 77 } 78 } 79 80 int get_rank(int x) 81 { 82 int ans = 0, now = root; 83 while(1) 84 { 85 if(x < w[now]) now = son[now][0]; 86 else 87 { 88 ans += size[son[now][0]]; 89 if(w[now] == x) 90 { 91 splay(now); 92 return ans + 1; 93 } 94 ans += cnt[now]; 95 now = son[now][1]; 96 } 97 } 98 } 99 100 int get_kth(int x) 101 { 102 int now = root; 103 while(1) 104 { 105 if(x <= size[son[now][0]]) now = son[now][0]; 106 else 107 { 108 x -= size[son[now][0]]; 109 if(x <= cnt[now]) 110 { 111 splay(now); 112 return w[now]; 113 } 114 x -= cnt[now]; 115 now = son[now][1]; 116 } 117 } 118 } 119 120 int get_pre() 121 { 122 int now = son[root][0]; 123 while(son[now][1]) now = son[now][1]; 124 return now; 125 } 126 127 int get_suc() 128 { 129 int now = son[root][1]; 130 while(son[now][0]) now = son[now][0]; 131 return now; 132 } 133 134 void del(int x) 135 { 136 int oldroot, leftbig, wh = get_rank(x); 137 if(cnt[root] > 1) 138 { 139 cnt[root]--; 140 update(root); 141 return; 142 } 143 if(!son[root][0] && !son[root][1]) 144 { 145 clear(root); 146 root = 0; 147 return; 148 } 149 if(!son[root][0] || !son[root][1]) 150 { 151 oldroot = root; 152 root = son[root][0] + son[root][1]; 153 f[root] = 0; 154 clear(oldroot); 155 return; 156 } 157 oldroot = root; 158 leftbig = get_pre(); 159 splay(leftbig); 160 son[root][1] = son[oldroot][1]; 161 f[son[root][1]] = root; 162 clear(oldroot); 163 update(root); 164 return; 165 } 166 167 int main() 168 { 169 int i, opt, x; 170 scanf("%d", &n); 171 for(i = 1; i <= n; i++) 172 { 173 scanf("%d %d", &opt, &x); 174 switch(opt) 175 { 176 case 1: insert(x); break; 177 case 2: del(x); break; 178 case 3: printf("%d\n", get_rank(x)); break; 179 case 4: printf("%d\n", get_kth(x)); break; 180 case 5: insert(x); printf("%d\n", w[get_pre()]); del(x); break; 181 case 6: insert(x); printf("%d\n", w[get_suc()]); del(x); break; 182 } 183 } 184 return 0; 185 }
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 #define N 300005 7 #define INF 2100000000 8 9 int n, m, d, root, sz, aa, bb; 10 int a[N], f[N], son[N][2], size[N], Min[N], key[N], add[N], rev[N]; 11 char opt[20]; 12 13 //删除 14 inline void clear(int x) 15 { 16 f[x] = son[x][0] = son[x][1] = size[x] = Min[x] = key[x] = add[x] = rev[x] = 0; 17 } 18 19 //判断 x 是父亲的哪个儿子 20 inline int get(int x) 21 { 22 return son[f[x]][1] == x; 23 } 24 25 inline void update(int x) 26 { 27 size[x] = size[son[x][0]] + size[son[x][1]] + 1; 28 Min[x] = key[x]; 29 if(son[x][0]) Min[x] = min(Min[x], Min[son[x][0]]); 30 if(son[x][1]) Min[x] = min(Min[x], Min[son[x][1]]); 31 } 32 33 //建树 34 inline int build(int l, int r, int fa) 35 { 36 if(l > r) return 0; 37 int mid = (l + r) >> 1, now = ++sz; 38 f[sz] = fa, key[sz] = a[mid]; 39 int lch = build(l, mid - 1, now); 40 int rch = build(mid + 1, r, now); 41 son[now][0] = lch, son[now][1] = rch; 42 update(now); 43 return now; 44 } 45 46 //标记下放 47 inline void pushdown(int x) 48 { 49 if(!x) return; 50 if(rev[x]) 51 { 52 rev[son[x][0]] ^= 1; 53 rev[son[x][1]] ^= 1; 54 swap(son[x][0], son[x][1]); 55 rev[x] = 0; 56 } 57 if(add[x]) 58 { 59 add[son[x][0]] += add[x], add[son[x][1]] += add[x]; 60 Min[son[x][0]] += add[x], Min[son[x][1]] += add[x]; 61 key[son[x][0]] += add[x], key[son[x][1]] += add[x]; 62 add[x] = 0; 63 } 64 } 65 66 //查找排名为 x 的数 67 inline int find(int x) 68 { 69 int now = root; 70 while(1) 71 { 72 pushdown(now); 73 if(x <= size[son[now][0]]) now = son[now][0]; 74 else 75 { 76 x -= size[son[now][0]]; 77 if(x == 1) return now; 78 x--; 79 now = son[now][1]; 80 } 81 } 82 } 83 84 //旋转 85 inline void rotate(int x) 86 { 87 pushdown(f[x]); 88 pushdown(x); 89 int old = f[x], oldf = f[old], wh = get(x); 90 son[old][wh] = son[x][wh ^ 1]; 91 f[son[old][wh]] = old; 92 son[x][wh ^ 1] = old; 93 f[old] = x; 94 if(oldf) son[oldf][son[oldf][1] == old] = x; 95 f[x] = oldf; 96 update(old); 97 update(x); 98 } 99 100 //mplay ?? 101 inline void splay(int x,int to) 102 { 103 for(int fa; (fa = f[x]) != to; rotate(x)) 104 if(f[fa] != to) 105 rotate(get(fa) == get(x) ? fa : x); 106 if(!to) root = x; 107 } 108 109 //区间加 110 inline void Add(int x, int y) 111 { 112 if(x > y) swap(x, y); 113 aa = find(x);//第 x - 1 个到根节点 114 bb = find(y + 2);//第 y + 1 个到根节点右子树 115 splay(aa, 0); 116 splay(bb, aa); 117 Min[son[son[root][1]][0]] += d; 118 add[son[son[root][1]][0]] += d; 119 key[son[son[root][1]][0]] += d; 120 update(son[root][1]); 121 update(root); 122 } 123 124 //区间翻转 125 inline void reverse(int x, int y) 126 { 127 if(x == y) return; 128 if(x > y) swap(x ,y); 129 aa = find(x); 130 bb = find(y + 2); 131 splay(aa, 0); 132 splay(bb, aa); 133 rev[son[son[root][1]][0]] ^= 1; 134 } 135 136 //区间向右平移 t 位 137 //先把 t % 区间长度,防止平移回来 138 //这样就相当于把后面的 t 个数字挪到区间前面来 139 //先把 [ y - t + 1, y ] 旋转出来 140 //然后插入到 [ x, y - t ] 区间的前面 141 inline void revolve(int x, int y, int t) 142 { 143 if(x > y) swap(x, y); 144 t %= y - x + 1; 145 if(!t) return; //平移回初始。。 146 aa = find(y - t + 1); 147 bb = find(y + 2); 148 splay(aa, 0); 149 splay(bb, aa); 150 int now = son[son[root][1]][0]; 151 son[son[root][1]][0] = 0; 152 update(son[root][1]); 153 update(root); 154 aa = find(x); 155 bb = find(x + 1); 156 splay(aa, 0); 157 splay(bb, aa); 158 son[son[root][1]][0] = now; 159 f[now] = son[root][1]; 160 update(son[root][1]); 161 update(root); 162 } 163 164 inline void insert(int x, int y) 165 { 166 aa = find(x + 1); 167 bb = find(x + 2); 168 splay(aa, 0); 169 splay(bb, aa); 170 son[son[root][1]][0] = ++sz; 171 f[sz] = son[root][1]; 172 key[sz] = Min[sz] = y; 173 size[sz] = 1; 174 update(son[root][1]); 175 update(root); 176 } 177 178 inline void del(int x) 179 { 180 aa = find(x); 181 bb = find(x + 2); 182 splay(aa, 0); 183 splay(bb, aa); 184 int now = son[son[root][1]][0]; 185 clear(now); 186 son[son[root][1]][0] = 0; 187 update(son[root][1]); 188 update(root); 189 } 190 191 inline int get_min(int x, int y) 192 { 193 if(x > y) swap(x, y); 194 aa = find(x); 195 bb = find(y + 2); 196 splay(aa, 0); 197 splay(bb, aa); 198 return Min[son[son[root][1]][0]]; 199 } 200 201 int main() 202 { 203 int i, j, x, y, z; 204 scanf("%d", &n); 205 a[1] = -INF, a[n + 2] = INF; 206 for(i = 1; i <= n; i++) scanf("%d", &a[i + 1]); 207 root = build(1, n + 2, 0); 208 scanf("%d", &m); 209 for(i = 1; i <= m; i++) 210 { 211 scanf("%s", opt); 212 switch(opt[0]) 213 { 214 case ‘A‘: scanf("%d %d %d", &x, &y, &d); Add(x, y); break; 215 case ‘R‘: 216 { 217 if(opt[3] == ‘E‘) 218 { 219 scanf("%d %d", &x, &y); 220 reverse(x, y); 221 } 222 else 223 { 224 scanf("%d %d %d", &x, &y, &z); 225 revolve(x, y, z); 226 } 227 break; 228 } 229 case ‘I‘: scanf("%d %d", &x, &y); insert(x, y); break; 230 case ‘D‘: scanf("%d", &x); del(x); break; 231 case ‘M‘: scanf("%d %d", &x, &y); printf("%d\n", get_min(x, y)); break; 232 } 233 } 234 return 0; 235 }
prim的heap优化
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #define heap pair<int, int> 5 6 using namespace std; 7 8 int n, m, cnt, ans; 9 int head[5001], to[400001], next[400001], val[400001]; 10 bool vis[5001]; 11 priority_queue <heap, vector <heap>, greater <heap> > q; 12 13 inline void add(int x, int y, int z) 14 { 15 to[cnt] = y; 16 val[cnt] = z; 17 next[cnt] = head[x]; 18 head[x] = cnt++; 19 } 20 21 inline void queue_prim() 22 { 23 int i, u, v, tot = n; 24 heap x; 25 q.push(make_pair(0, 1)); 26 while(!q.empty() && tot) 27 { 28 x = q.top(); 29 q.pop(); 30 u = x.second; 31 if(vis[u]) continue; 32 vis[u] = 1; 33 ans += x.first; 34 tot--; 35 for(i = head[u]; i != -1; i = next[i]) 36 { 37 v = to[i]; 38 if(!vis[v]) q.push(make_pair(val[i], v)); 39 } 40 } 41 } 42 43 int main() 44 { 45 int i, x, y, z; 46 memset(head, -1, sizeof(head)); 47 scanf("%d %d", &n, &m); 48 for(i = 1; i <= m; i++) 49 { 50 scanf("%d %d %d", &x, &y, &z); 51 add(x, y, z); 52 add(y, x, z); 53 } 54 queue_prim(); 55 printf("%d", ans); 56 return 0; 57 }
划分树求区间第k大
1 #include <cstdio> 2 #include <algorithm> 3 #define N 100001 4 5 using namespace std; 6 7 int n, m; 8 int c[N];//排序后数组 9 int num[20][N], val[20][N]; 10 //一共有20层,每一层元素排放,0层是原数组 11 //num[i] 表示 i 前面有多少个孩子进入左孩子 12 13 inline void build(int l, int r, int k) 14 { 15 if(l == r) return; 16 int i, mid = (l + r) / 2, lc = l, rc = mid + 1, isame = mid - l + 1; 17 //isame 表示有多少个和 c[mid] 相同的数进入左边,先假设全部进入左边 18 for(i = l; i <= r; i++) 19 if(val[k][i] < c[mid]) 20 isame--;//判断,把进入左边的且小于 c[mid] 的减去 21 for(i = l; i <= r; i++) 22 { 23 num[k][i] = i == l ? 0 : num[k][i - 1];//dp 24 if(val[k][i] < c[mid] || (val[k][i] == c[mid] && isame > 0))//进入左孩子 25 { 26 val[k + 1][lc++] = val[k][i]; 27 num[k][i]++; 28 if(val[k][i] == c[mid]) isame--; 29 } 30 else val[k + 1][rc++] = val[k][i];//到右孩子 31 } 32 build(l, mid, k + 1); 33 build(mid + 1, r, k + 1); 34 } 35 36 inline int query(int l, int r, int k, int ql, int qr, int qk) 37 { 38 if(l == r) return val[k][l];//叶子节点即为结果 39 int lnum = l == ql ? 0 : num[k][ql - 1], t = num[k][qr] - lnum, mid = (l + r) / 2;//左端点相同? 40 //lnum 表示 ql 前面有多少个数进入左孩子,t 表示 ql 和 qr 之间有多少个数进入左孩子 41 if(t >= qk) return query(l, mid, k + 1, l + lnum, l + num[k][qr] - 1, qk); 42 //全在左子树,去左子树搜索 43 else 44 { 45 //ql - l 表示 ql 前面有多少个数,再减去 lnum 表示有多少个数去了右子树 46 int kj = mid + 1 + (ql - l - lnum); 47 //ql - qr + 1 表示 ql 到 qr 有多少数,减去左边的,剩下的就是去右边的,去右边一个,下标就是kj,所以减一 48 return query(mid + 1, r, k + 1, kj, kj + qr - ql + 1 - t - 1, qk - t); 49 } 50 } 51 52 int main() 53 { 54 int i, x, y, z; 55 while(~scanf("%d %d", &n, &m)) 56 { 57 for(i = 1; i <= n; i++) scanf("%d", &val[0][i]), c[i] = val[0][i]; 58 sort(c + 1, c + n + 1); 59 build(1, n, 0); 60 for(i = 1; i <= m; i++) 61 { 62 scanf("%d %d %d", &x, &y, &z); 63 printf("%d\n", query(1, n, 0, x, y, z)); 64 } 65 } 66 return 0; 67 }
主席树
1 #include <cstdio> 2 #include <algorithm> 3 #define lc son[now][0], l, mid 4 #define rc son[now][1], mid + 1, r 5 6 using namespace std; 7 8 const int N = 100000 + 5; 9 10 int T, n, q, tot; 11 int a[N], b[N], son[20 * N][2], sum[20 * N], rt[20 * N]; 12 //主席树第 i 棵树存的是 a[0] - a[i] 的树(前缀和) 13 //主席树一个节点 sum 存的是当前线段所包含的数的个数。。晕。 14 //rt 是每一棵树的根节点编号 15 //a 保存原数组,b 保存排序后的数组 16 17 //注意 & 的使用 18 inline void build(int &now, int l, int r) 19 { 20 now = ++tot; 21 sum[now] = 0; 22 if(l == r) return; 23 int mid = (l + r) >> 1; 24 build(lc); 25 build(rc); 26 } 27 28 inline void update(int &now, int l, int r, int last, int x) 29 { 30 now = ++tot; 31 //继承上个节点的孩子 32 son[now][0] = son[last][0]; 33 son[now][1] = son[last][1]; 34 //继承上一个节点的 sum 并加上当前所加入的数的个数(就是1) 35 sum[now] = sum[last] + 1; 36 if(l == r) return; 37 int mid = (l + r) >> 1; 38 if(x <= mid) update(lc, son[now][0], x); 39 else update(rc, son[now][1], x); 40 } 41 42 //因为每个节点存的是有多少个数在当前区间 43 //求区间 [1,3] 即求 rt[3] - rt[0] 内的数 44 //可通过递归二分解决 45 inline int query(int s, int t, int l, int r, int x) 46 { 47 if(l == r) return l; 48 int mid = (l + r) >> 1, cnt = sum[son[t][0]] - sum[son[s][0]]; 49 if(x <= cnt) return query(son[s][0], son[t][0], l, mid, x); 50 else return query(son[s][1], son[t][1], mid + 1, r, x - cnt); 51 } 52 53 int main() 54 { 55 int i, x, y, z, sz; 56 scanf("%d", &T); 57 while(T--) 58 { 59 scanf("%d %d", &n, &q); 60 for(i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i]; 61 sort(b + 1, b + n + 1); 62 sz = unique(b + 1, b + n + 1) - (b + 1);//求不同的数一共有几个,即区间大小 63 tot = 0; 64 build(rt[0], 1, sz); 65 //用每个元素在 b 中的坐标重置 a 数组(离散化) 66 for(i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b; 67 for(i = 1; i <= n; i++) update(rt[i], 1, sz, rt[i - 1], a[i]); 68 while(q--) 69 { 70 scanf("%d %d %d", &x, &y, &z); 71 printf("%d\n", b[query(rt[x - 1], rt[y], 1, sz, z)]); 72 } 73 } 74 return 0; 75 }
AC自动机
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <queue> 5 #define N 500005 6 7 using namespace std; 8 9 char s[N << 1]; 10 int T, n, sz, ans; 11 int ch[N][26], val[N], fail[N]; 12 bool vis[N]; 13 queue <int> q; 14 15 inline void clear() 16 { 17 n = sz = ans = 0; 18 memset(ch, 0, sizeof(ch)); 19 memset(vis, 0, sizeof(vis)); 20 memset(val, 0, sizeof(val)); 21 memset(fail, 0, sizeof(fail)); 22 } 23 24 inline void insert() 25 { 26 int i, x, len = strlen(s), now = 0; 27 for(i = 0; i < len; i++) 28 { 29 x = s[i] - ‘a‘; 30 if(!ch[now][x]) ch[now][x] = ++sz; 31 now = ch[now][x]; 32 } 33 val[now]++; 34 } 35 36 inline void make_fail() 37 { 38 int i, now; 39 while(!q.empty()) q.pop(); 40 //第二层特殊处理,将第二层节点的 fail 指针指向 root(其实就是0,也就不用管) 41 for(i = 0; i < 26; i++) 42 if(ch[0][i]) 43 q.push(ch[0][i]); 44 while(!q.empty()) 45 { 46 now = q.front(), q.pop(); 47 for(i = 0; i < 26; i++) 48 { 49 if(!ch[now][i]) 50 { 51 ch[now][i] = ch[fail[now]][i]; 52 continue; 53 } 54 fail[ch[now][i]] = ch[fail[now]][i]; 55 q.push(ch[now][i]); 56 } 57 } 58 } 59 60 inline void ac() 61 { 62 int x, y, len = strlen(s), i, now = 0; 63 for(i = 0; i < len; i++) 64 { 65 vis[now] = 1; 66 x = s[i] - ‘a‘; 67 y = ch[now][x]; 68 while(y && !vis[y]) 69 { 70 vis[y] = 1; 71 ans += val[y]; 72 y = fail[y]; 73 } 74 now = ch[now][x]; 75 } 76 } 77 78 int main() 79 { 80 int i; 81 scanf("%d", &T); 82 while(T--) 83 { 84 clear(); 85 scanf("%d", &n); 86 for(i = 1; i <= n; i++) 87 { 88 scanf("%s", s); 89 insert(); 90 } 91 scanf("%s", s); 92 make_fail(); 93 ac(); 94 printf("%d\n", ans); 95 } 96 return 0; 97 }
以上是关于模板集合的主要内容,如果未能解决你的问题,请参考以下文章