当前位置:   article > 正文

NOIp 2018 前的数据结构板子总结

fenwick是什么板子

Disjoint-set data structure

通过路径压缩实现最常用的并查集

题目链接: Luogu P3367 【模板】并查集

时间复杂度:

  1. MakeSet: Θ(n)
  2. Find: Θ(α(n))
  3. Union: Θ(α(n))
    (α(n)为inverse Ackermann function)
  1. #include <cstdio>
  2. int parent[10010];
  3. int n, m, opt, u, v;
  4. inline void MakeSet(const int &maximum) {
  5. for (register int i(0); i <= maximum; ++i) {
  6. parent[i] = i;
  7. }
  8. }
  9. inline int Find(const int &cur) {
  10. return cur == parent[cur] ? cur : parent[cur] = Find(parent[cur]);
  11. }
  12. inline void Union(const int &x, const int &y) {
  13. parent[Find(y)] = Find(x);
  14. }
  15. int main(int argc, char const *argv[]) {
  16. scanf("%d %d", &n, &m);
  17. MakeSet(n);
  18. while (m--) {
  19. scanf("%d %d %d", &opt, &u, &v);
  20. switch (opt) {
  21. case 1: {
  22. Union(u, v);
  23. break;
  24. }
  25. case 2: {
  26. puts(Find(u) == Find(v) ? "Y" : "N");
  27. break;
  28. }
  29. }
  30. }
  31. return 0;
  32. }

Heap

由于C++中包含用堆实现的priority_queue, 就不手写了。

题目链接: Luogu P3378 【模板】堆

时间复杂度:

  1. top: Θ(1)
  2. pop: Θ(lgn)
  3. push: Θ(lgn)
  1. #include <queue>
  2. #include <cstdio>
  3. std::priority_queue<int, std::vector<int>, std::greater<int> > Q;
  4. int n, opt, x;
  5. int main(int argc, char const *argv[]) {
  6. scanf("%d", &n);
  7. while (n--) {
  8. scanf("%d", &opt);
  9. switch (opt) {
  10. case 1: {
  11. scanf("%d", &x);
  12. Q.push(x);
  13. break;
  14. }
  15. case 2: {
  16. printf("%d\n", Q.top());
  17. break;
  18. }
  19. case 3: {
  20. Q.pop();
  21. break;
  22. }
  23. }
  24. }
  25. return 0;
  26. }

Fenwick tree

也叫binary indexed tree

时间复杂度:

  1. LOWBIT: Θ(1)
  2. Sum: Θ(lgn)
  3. Add: Θ(lgn)
单点修改 + 区间查询

题目链接: Luogu P3374 【模板】树状数组 1

  1. #include <cstdio>
  2. #define LOWBIT(a) (a & -a)
  3. int fenwick_tree[500010], n;
  4. inline int Sum(register int index) {
  5. register int ret(0);
  6. while (index) {
  7. ret += fenwick_tree[index],
  8. index -= LOWBIT(index);
  9. }
  10. return ret;
  11. }
  12. inline void Add(register int index, const int &kDelta) {
  13. while (index <= n) {
  14. fenwick_tree[index] += kDelta,
  15. index += LOWBIT(index);
  16. }
  17. }
  18. #undef LOWBIT
  19. int m, opt, x, y;
  20. int main(int argc, char const *argv[]) {
  21. scanf("%d %d", &n, &m);
  22. for (register int i(1); i <= n; ++i) {
  23. scanf("%d", &y),
  24. Add(i, y);
  25. }
  26. while (m--) {
  27. scanf("%d %d %d", &opt, &x, &y);
  28. switch (opt) {
  29. case 1: {
  30. Add(x, y);
  31. break;
  32. }
  33. case 2: {
  34. printf("%d\n", Sum(y) - Sum(x - 1));
  35. break;
  36. }
  37. }
  38. }
  39. return 0;
  40. }
区间修改 + 单点查询

原树状数组的基础上使用差分。

题目链接: Luogu P3368 【模板】树状数组 2

  1. #include <cstdio>
  2. #define LOWBIT(a) (a & -a)
  3. int fenwick_tree[500010], n;
  4. inline int Sum(register int index) {
  5. register int ret(0);
  6. while (index) {
  7. ret += fenwick_tree[index],
  8. index -= LOWBIT(index);
  9. }
  10. return ret;
  11. }
  12. inline void Add(register int index, const int &kDelta) {
  13. while (index <= n) {
  14. fenwick_tree[index] += kDelta,
  15. index += LOWBIT(index);
  16. }
  17. }
  18. #undef LOWBIT
  19. int m, opt, x, y, k;
  20. int main(int argc, char const *argv[]) {
  21. scanf("%d %d", &n, &m);
  22. for (register int i(1); i <= n; ++i) {
  23. scanf("%d", &y),
  24. Add(i, y - x),
  25. x = y;
  26. }
  27. while (m--) {
  28. scanf("%d %d", &opt, &x);
  29. switch (opt) {
  30. case 1: {
  31. scanf("%d %d", &y, &k);
  32. Add(x, k), Add(y + 1, -k);
  33. break;
  34. }
  35. case 2: {
  36. printf("%d\n", Sum(x));
  37. break;
  38. }
  39. }
  40. }
  41. return 0;
  42. }
区间修改 + 区间查询

仍然使用了差分, 推导过程如下:

设原数组为an, 考虑一个差分数组bn=anan1(约定b0=0), 可以得到

i=1nai=i=1nj=1ibi=i=1ni+1bi=i=1n(n+1i)bi=(n+1)i=1nbii=1nibi

写的简单易懂就是

a1+a2++an1+an= b1+(b1+b2)++(b1+b2++bn2+bn1)+(b1+b2++bn1+bn)= nb1+(n1)b2++2bn1+bn= (n+11)b1+(n+12)b2++[n+1(n1)]bn1+(n+1n)bn= (n+1)(b1+b2++bn1+bn)(b1+2b2++(n1)bn1+nbn)

biibi分别用两个树状数组维护就可以了。

题目链接: Luogu P3372 【模板】线段树 1

  1. #include <cstdio>
  2. #define LOWBIT(a) (a & -a)
  3. long long fenwick_tree1[100010], fenwick_tree2[100010];
  4. int n;
  5. inline long long Sum(const long long *fenwick_tree, register int index) {
  6. register long long ret(0ll);
  7. while (index) {
  8. ret += fenwick_tree[index],
  9. index -= LOWBIT(index);
  10. }
  11. return ret;
  12. }
  13. inline void Add(register long long *fenwick_tree, register int index, const long long &kDelta) {
  14. while (index <= n) {
  15. fenwick_tree[index] += kDelta,
  16. index += LOWBIT(index);
  17. }
  18. }
  19. #undef LOWBIT
  20. int m, opt;
  21. long long x, y, k;
  22. int main(int argc, char const *argv[]) {
  23. scanf("%d %d", &n, &m);
  24. for (register int i(1); i <= n; ++i) {
  25. scanf("%lld", &y);
  26. Add(fenwick_tree1, i, y - x), Add(fenwick_tree2, i, i * (y - x));
  27. x = y;
  28. }
  29. while (m--) {
  30. scanf("%d %lld %lld", &opt, &x, &y);
  31. switch (opt) {
  32. case 1: {
  33. scanf("%lld", &k);
  34. Add(fenwick_tree1, x, k), Add(fenwick_tree1, y + 1, -k),
  35. Add(fenwick_tree2, x, x * k), Add(fenwick_tree2, y + 1, (y + 1) * -k);
  36. break;
  37. }
  38. case 2: {
  39. printf("%lld\n", (y + 1) * Sum(fenwick_tree1, y) - x * Sum(fenwick_tree1, x - 1) - (Sum(fenwick_tree2, y) - Sum(fenwick_tree2, x - 1)));
  40. break;
  41. }
  42. }
  43. }
  44. return 0;
  45. }

Segment tree

只放一个板子题吧, 记住框架就行。

时间复杂度:

  1. Construct: Θ(nlgn)
  2. Update: Θ(1)
  3. PushDown: Θ(1)
  4. Sum: Ω(lgn)
  5. Query: Ω(lgn)

题目链接: Luogu P3372 【模板】线段树 1

  1. #include <cstdio>
  2. struct SegmentTree {
  3. long long value, delta;
  4. int l, r;
  5. SegmentTree *left, *right;
  6. SegmentTree(const long long &val = 0) {
  7. value = val, delta = 0;
  8. left = right = nullptr;
  9. l = r = 0;
  10. }
  11. inline void Update() {
  12. this->value = (this->left ? this->left->value : 0ll) + (this->right ? this->right->value : 0ll);
  13. }
  14. inline void PushDown() {
  15. if (this->delta && this->left) {
  16. this->left->value += (this->left->r - this->left->l + 1) * this->delta,
  17. this->right->value += (this->right->r - this->right->l + 1) * this->delta;
  18. this->left->delta += this->delta,
  19. this->right->delta += this->delta;
  20. this->delta = 0ll;
  21. }
  22. }
  23. SegmentTree(const int &le, const int &ri) {
  24. this->l = le, this->r = ri;
  25. this->left = this->right = nullptr;
  26. this->delta = 0ll;
  27. if (le == ri) {
  28. scanf("%lld", &this->value);
  29. } else {
  30. register int mid((le + ri) >> 1);
  31. this->left = new SegmentTree(le, mid);
  32. this->right = new SegmentTree(mid + 1, ri);
  33. this->Update();
  34. }
  35. }
  36. ~SegmentTree() {
  37. if (left) {
  38. delete left;
  39. delete right;
  40. }
  41. }
  42. inline long long Query(const int &le, const int &ri) {
  43. if (le <= this->l && this->r <= ri) {
  44. return this->value;
  45. } else {
  46. this->PushDown();
  47. register int mid((this->l + this->r) >> 1);
  48. return (le <= mid ? this->left->Query(le, ri) : 0ll) + (mid < ri ? this->right->Query(le, ri) : 0ll);
  49. }
  50. }
  51. inline void Add(const int &le, const int &ri, const long long &kDelta) {
  52. if (le <= this->l && this->r <= ri) {
  53. this->value += (this->r - this->l + 1) * kDelta,
  54. this->delta += kDelta;
  55. } else {
  56. this->PushDown();
  57. register int mid((this->l + this->r) >> 1);
  58. if (le <= mid) {
  59. this->left->Add(le, ri, kDelta);
  60. }
  61. if (mid < ri) {
  62. this->right->Add(le, ri, kDelta);
  63. }
  64. this->Update();
  65. }
  66. }
  67. } *root;
  68. int n, m, opt, x, y;
  69. long long k;
  70. int main(int argc, char const *argv[]) {
  71. scanf("%d %d", &n, &m);
  72. root = new SegmentTree(1, n);
  73. while (m--) {
  74. scanf("%d %d %d", &opt, &x, &y);
  75. switch (opt) {
  76. case 1: {
  77. scanf("%lld", &k);
  78. root->Add(x, y, k);
  79. break;
  80. }
  81. case 2: {
  82. printf("%lld\n", root->Query(x, y));
  83. break;
  84. }
  85. }
  86. }
  87. return 0;
  88. }

Heavy path decomposition

别人都放在图论里, 就我一个放在数据结构里。

推荐博客: 树链剖分详解

题目链接: Luogu P3384 【模板】树链剖分

时间复杂度:

  1. Dfs1: Θ(n)
  2. Dfs2: Θ(n)
  3. PathModify: Ω(lgn)
  4. PathGetSum: Ω(lgn)
  5. SubTreeModify: Ω(lgn)
  6. SubTreeGetSum: Ω(lgn)

可以换用树状数组, 常数更小。

  1. #include <cstdio>
  2. #include <vector>
  3. #include <algorithm>
  4. struct Node {
  5. long long value, delta;
  6. Node *left, *right;
  7. int l, r;
  8. Node() {
  9. left = right = nullptr;
  10. l = r = delta = value = 0;
  11. }
  12. ~Node() {
  13. if (left) {
  14. delete left;
  15. delete right;
  16. }
  17. }
  18. } *root;
  19. std::vector<int> adj[100010];
  20. int heavy[100010], size[100010], father[100010], top[100010], depth[100010];
  21. int new_index[100010];
  22. long long original_value[100010], indexed_value[100010];
  23. int maxtop, n, m;
  24. long long kMod;
  25. void Dfs1(const int &cur, const int &fathernode) {
  26. father[cur] = fathernode,
  27. depth[cur] = depth[fathernode] + 1,
  28. size[cur] = 1;
  29. for (auto i : adj[cur]) {
  30. if (i != fathernode) {
  31. Dfs1(i, cur),
  32. size[cur] += size[i];
  33. if (size[i] > size[heavy[cur]]) heavy[cur] = i;
  34. }
  35. }
  36. }
  37. void Dfs2(const int &cur, const int &topnode) {
  38. static int index_count(0);
  39. new_index[cur] = ++index_count,
  40. indexed_value[index_count] = original_value[cur];
  41. top[cur] = topnode;
  42. if (heavy[cur]) {
  43. Dfs2(heavy[cur], topnode);
  44. for (auto i : adj[cur]) {
  45. if (i != father[cur] && i != heavy[cur]) {
  46. Dfs2(i, i);
  47. }
  48. }
  49. }
  50. }
  51. #define PUSH_UP(cur) cur->value = cur->left->value + cur->right->value;
  52. void Build(Node *cur, const int &l, const int &r) {
  53. if (l == r) {
  54. cur->value = indexed_value[l];
  55. cur->l = cur->r = l;
  56. } else {
  57. cur->left = new Node, cur->right = new Node;
  58. register int mid((l + r) >> 1);
  59. cur->l = l, cur->r = r;
  60. Build(cur->left, l, mid), Build(cur->right, mid + 1, r);
  61. PUSH_UP(cur);
  62. }
  63. }
  64. #define PUSH_DOWN(cur) if (cur->delta) {cur->left->value = (cur->left->value + cur->delta * (cur->left->r - cur->left->l + 1)) % kMod, cur->right->value = (cur->right->value + cur->delta * (cur->right->r - cur->right->l + 1)) % kMod, cur->left->delta = (cur->left->delta + cur->delta) % kMod, cur->right->delta = (cur->right->delta + cur->delta) % kMod, cur->delta = 0;}
  65. void Modify(Node *cur, const int &l, const int &r, const long long &kDelta) {
  66. if (l <= cur->l && cur->r <= r) {
  67. cur->value += kDelta* ((cur->r - cur->l) + 1),
  68. cur->delta += kDelta;
  69. } else {
  70. PUSH_DOWN(cur);
  71. register int mid((cur->l + cur->r) >> 1);
  72. if (l <= mid) Modify(cur->left, l, r, kDelta);
  73. if (mid < r) Modify(cur->right, l, r, kDelta);
  74. PUSH_UP(cur);
  75. }
  76. }
  77. long long GetSum(Node *cur, const int &l, const int &r) {
  78. if (l <= cur->l && cur->r <= r) {
  79. return cur->value;
  80. } else {
  81. PUSH_DOWN(cur);
  82. register long long ret(0);
  83. register int mid((cur->l + cur->r) >> 1);
  84. if (l <= mid) (ret += GetSum(cur->left, l, r)) %= kMod;
  85. if (mid < r) (ret += GetSum(cur->right, l, r)) %= kMod;
  86. return ret % kMod;
  87. }
  88. }
  89. inline void PathModify(const int &x, const int &y, const long long &kDelta) {
  90. register int a(x), b(y);
  91. while (top[a] != top[b]) {
  92. if (depth[top[a]] < depth[top[b]]) std::swap(a, b);
  93. Modify(root, new_index[top[a]], new_index[a], kDelta);
  94. a = father[top[a]];
  95. }
  96. if (depth[a] > depth[b]) std::swap(a, b);
  97. Modify(root, new_index[a], new_index[b], kDelta);
  98. }
  99. inline long long PathGetSum(const int &x, const int &y) {
  100. register int a(x), b(y);
  101. register long long ret(0ll);
  102. while (top[a] != top[b]) {
  103. if (depth[top[a]] < depth[top[b]]) std::swap(a, b);
  104. (ret += GetSum(root, new_index[top[a]], new_index[a])) %= kMod;
  105. a = father[top[a]];
  106. }
  107. if (depth[a] > depth[b]) std::swap(a, b);
  108. return (ret + GetSum(root, new_index[a], new_index[b])) % kMod;
  109. }
  110. inline void SubTreeModify(const int &x, const long long &kDelta) {
  111. Modify(root, new_index[x], new_index[x] + size[x] - 1, kDelta);
  112. }
  113. inline long long SubTreeGetSum(const int &x) {
  114. return GetSum(root, new_index[x], new_index[x] + size[x] - 1);
  115. }
  116. int main(int argc, char const *argv[]) {
  117. scanf("%d %d %d %lld", &n, &m, &maxtop, &kMod);
  118. for (int i(1); i <= n; ++i) scanf("%lld", &original_value[i]);
  119. for (int i(1), u, v; i < n; ++i) {
  120. scanf("%d %d", &u, &v),
  121. adj[u].push_back(v),
  122. adj[v].push_back(u);
  123. }
  124. Dfs1(maxtop, maxtop), Dfs2(maxtop, maxtop),
  125. root = new Node,
  126. Build(root, 1, n);
  127. long long z;
  128. register int opt, x, y;
  129. while (m--) {
  130. scanf("%d", &opt);
  131. switch (opt) {
  132. case 1: {
  133. scanf("%d %d %lld", &x, &y, &z),
  134. PathModify(x, y, z % kMod);
  135. break;
  136. }
  137. case 2: {
  138. scanf("%d %d", &x, &y),
  139. printf("%lld\n", PathGetSum(x, y));
  140. break;
  141. }
  142. case 3: {
  143. scanf("%d %lld", &x, &z),
  144. SubTreeModify(x, z % kMod);
  145. break;
  146. }
  147. case 4: {
  148. scanf("%d", &x),
  149. printf("%lld\n", SubTreeGetSum(x));
  150. break;
  151. }
  152. }
  153. }
  154. return 0;
  155. }

转载于:https://www.cnblogs.com/forth/p/9771575.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/913698
推荐阅读
相关标签
  

闽ICP备14008679号