首页 > 代码库 > 模板集合

模板集合

读入优化

 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 }

 

模板集合