[关闭]
@LinKArftc 2015-10-06T02:40:07.000000Z 字数 8877 阅读 1055

线段树

数据结构


一张图说明什么事线段树
线段树通常配合离散化,解决 RMQ 问题,这是一种很神奇的数据结构 = =||。。。

poj 2299

题意:

线段树求逆序对个数

  1. /*
  2. ID: LinKArftc
  3. PROG: 2299.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. #define MAXN 500010
  12. int num[MAXN], arr[MAXN], tree[MAXN << 2];
  13. int query(int val, int l, int r, int rt) {
  14. if (l == r) {
  15. tree[rt] ++;
  16. return 0;
  17. }
  18. int mid = (l + r) >> 1;
  19. int ret = 0;
  20. if (val <= arr[mid]) ret = query(val, l, mid, rt << 1) + tree[rt << 1 | 1];
  21. else ret = query(val, mid + 1, r, rt << 1 | 1);
  22. tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
  23. return ret;
  24. }
  25. int main() {
  26. // freopen("input.txt", "r", stdin);
  27. int n;
  28. while (~scanf("%d", &n) && n) {
  29. for (int i = 1; i <= n; i ++) {
  30. scanf("%d", &num[i]);
  31. arr[i] = num[i];
  32. }
  33. sort(arr + 1, arr + n + 1);
  34. int tol = 1;
  35. for (int i = 2; i <= n; i ++) {
  36. if (arr[i] != arr[tol]) arr[++ tol] = arr[i];
  37. }
  38. memset(tree, 0, sizeof(tree));
  39. long long ans = 0;
  40. for (int i = 1; i <= n; i ++) ans += query(num[i], 1, tol, 1);
  41. printf("%lld\n", ans);
  42. }
  43. return 0;
  44. }

poj 2352

题目大意:给你星星的坐标(y递增,若y相等,x递增),每个星星都有一个等级,规定它的等级就是在它左下方的星星的个数。输入所有星星后,依次输出等级为0到n-1的星星的个数。

  1. /*
  2. ID: LinKArftc
  3. PROG: 2352.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. #define N 15010
  12. #define M 32010
  13. int tree[M << 2], cnt[N];
  14. int query(int rt, int l, int r, int L, int R) {
  15. int mid = (l + r) >> 1;
  16. if (L <= l && R >= r) return tree[rt];
  17. int ret = 0;
  18. if (L <= mid) ret += query(rt << 1, l, mid, L, R);
  19. if (R > mid) ret += query(rt << 1 | 1, mid + 1, r, L, R);
  20. return ret;
  21. }
  22. void update(int rt, int l, int r, int x) {
  23. if (l == r) {
  24. tree[rt] ++;
  25. return;
  26. }
  27. int mid = (l + r) >> 1;
  28. if (x <= mid) update(rt << 1, l, mid, x);
  29. else update(rt << 1 | 1, mid + 1, r, x);
  30. tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
  31. }
  32. int main() {
  33. // freopen("input.txt", "r", stdin);
  34. int n;
  35. int x, y;
  36. while (~scanf("%d", &n)) {
  37. memset(cnt, 0, sizeof(cnt));
  38. memset(tree, 0, sizeof(tree));
  39. for (int i = 1; i <= n; i ++) {
  40. scanf("%d %d", &x, &y);
  41. x ++;
  42. cnt[query(1, 1, M - 5, 1, x)] ++;
  43. update(1, 1, M - 5, x);
  44. }
  45. for (int i = 0; i < n; i ++) {
  46. printf("%d\n", cnt[i]);
  47. }
  48. }
  49. return 0;
  50. }

poj 2528

区间覆盖
很经典的一道题,不知道哪里写挫了导致 RE,最近写线段树写死了,不想碰了,以后再改。
已经改好了,其实很简单,之前怎么就那么傻逼。。。= =(详见后文)

  1. /*
  2. ID: LinKArftc
  3. PROG: 2528.cpp
  4. LANG: C++
  5. */
  6. ////// RE
  7. //
  8. #include <cstdio>
  9. #include <iostream>
  10. #include <cstring>
  11. #include <algorithm>
  12. using namespace std;
  13. const int maxn = 10010;
  14. struct Node {
  15. int l, r, id;
  16. } post[maxn];
  17. int vis[maxn], ans;
  18. int ll[maxn], rr[maxn];
  19. bool cmp_l(Node a, Node b) { return a.l < b.l; }
  20. bool cmp_r(Node a, Node b) { return a.r < b.r; }
  21. bool cmp_id(Node a, Node b) { return a.id < b.id; }
  22. struct Post {
  23. bool isCover;
  24. int l, r;
  25. } tree[maxn << 2];
  26. void build(int rt, int l, int r) {
  27. tree[rt].isCover = 0;
  28. tree[rt].l = l;
  29. tree[rt].r = r;
  30. int mid = (l + r) >> 1;
  31. if (l == r) return ;
  32. else {
  33. build(rt << 1, l, mid);
  34. build(rt << 1 | 1, mid + 1, r);
  35. }
  36. }
  37. bool check(int rt, int l, int r) {
  38. if (tree[rt].isCover == true) return false;
  39. if (tree[rt].l == l && tree[rt].r == r) {
  40. tree[rt].isCover = 1;
  41. return true;
  42. }
  43. bool rec;
  44. int mid = (tree[rt].l + tree[rt].r) >> 1;
  45. if (r <= mid) rec = check(rt << 1, l, r);
  46. else if (l > mid) rec = check(rt << 1 | 1, l, r);
  47. else {
  48. bool r1 = check(rt << 1, l, mid);
  49. bool r2 = check(rt << 1 | 1, mid + 1, r);
  50. rec = r1 || r2;
  51. }
  52. if (tree[rt << 1].isCover && tree[rt << 1 | 1].isCover) tree[rt].isCover = true;
  53. return rec;
  54. }
  55. int main() {
  56. freopen("input.txt", "r", stdin);
  57. int T;
  58. scanf("%d", &T);
  59. while (T --) {
  60. int n;
  61. scanf("%d", &n);
  62. for (int i = 1; i <= n; i ++) {
  63. scanf("%d %d", &post[i].l, &post[i].r);
  64. post[i].id = i;
  65. ll[i] = post[i].l;
  66. rr[i] = post[i].r;
  67. }
  68. sort(post + 1, post + n + 1, cmp_l);
  69. int cur = 1;
  70. ll[1] = 1;
  71. for (int i = 2; i <= n; i ++) {
  72. if (post[i].l == post[i-1].l) ll[i] = cur;
  73. else ll[i] = ++ cur;
  74. }
  75. for (int i = 1; i <= n; i ++) {
  76. post[i].l = ll[i];
  77. }
  78. sort(post + 1, post + n + 1, cmp_r);
  79. cur = 1;
  80. rr[1] = 1;
  81. for (int i = 2; i <= n; i ++) {
  82. if (post[i].r == post[i-1].r) {
  83. rr[i] = cur;
  84. }
  85. else rr[i] = ++ cur;
  86. }
  87. for (int i = 1; i <= n; i ++) {
  88. post[i].r = rr[i];
  89. }
  90. sort(post + 1, post + n + 1, cmp_id);
  91. build(1, 1, n);
  92. for (int i = n; i >= 1; i --) {
  93. if (check(1, post[i].l, post[i].r)) {
  94. ans ++;
  95. }
  96. }
  97. printf("%d\n", ans);
  98. }
  99. return 0;
  100. }
  1. /*
  2. ID: LinKArftc
  3. PROG: setment_tree.cpp
  4. LANG: C++
  5. */
  6. /*
  7. *矩形覆盖为连续型线段树,注意和离散型线段树的区别
  8. */
  9. typedef long long ll;
  10. const int maxn = 200010;
  11. int tree[maxn << 2];
  12. int num[maxn];
  13. int n, len;
  14. map <int, int> mp;
  15. set <int> st;
  16. set <int> ans;
  17. struct Node {
  18. int a, b;
  19. int id;
  20. } post[maxn];
  21. void update(int rt, int l, int r, int L, int R, int id) {
  22. if (tree[rt]) {
  23. tree[rt << 1] = tree[rt];
  24. tree[rt << 1 | 1] = tree[rt];
  25. tree[rt] = 0;
  26. }
  27. if (L <= l && R >= r) {
  28. tree[rt] = id;
  29. return;
  30. }
  31. int mid = (l + r) >> 1;
  32. if (R <= mid) update(rt << 1, l, mid, L, R, id);
  33. else if (L >= mid) update(rt << 1 | 1, mid, r, L, R, id);
  34. else {
  35. update(rt << 1, l, mid, L, R, id);
  36. update(rt << 1 | 1, mid, r, L, R, id);
  37. }
  38. }
  39. void query(int rt, int l, int r) {
  40. if (tree[rt]) {
  41. ans.insert(tree[rt]);
  42. return;
  43. }
  44. if (r - l <= 1) return;
  45. int mid = (l + r) >> 1;
  46. query(rt << 1, l, mid);
  47. query(rt << 1 | 1, mid, r);
  48. }
  49. int main() {
  50. //input;
  51. memset(tree, 0, sizeof(tree));
  52. scanf("%d %d", &n, &len);
  53. for (int i = 1; i <= n; i ++) {
  54. scanf("%d %d", &post[i].a, &post[i].b);
  55. post[i].id = i;
  56. st.insert(post[i].a);
  57. st.insert(post[i].b);
  58. }
  59. int cur = 1;
  60. while (!st.empty()) {
  61. mp[*st.begin()] = cur ++;
  62. st.erase(st.begin());
  63. }
  64. for (int i = 1; i <= n; i ++) {
  65. post[i].a = mp[post[i].a];
  66. post[i].b = mp[post[i].b];
  67. }
  68. for (int i = 1; i <= n; i ++) {
  69. update(1, 1, n << 1, post[i].a, post[i].b, post[i].id);
  70. }
  71. query(1, 1, n << 1);
  72. printf("%d\n", ans.size());
  73. return 0;
  74. }

poj 3264

求区间最大值最小值
裸线段树,配合离散化

  1. /*
  2. ID: LinKArftc
  3. PROG: 3264.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. #define N 50005
  12. int num[N], arr[N];
  13. struct Node {
  14. int ma, mi;
  15. } tree[N << 2];
  16. void build(int rt, int l, int r) {
  17. if (l == r) {
  18. tree[rt].ma = num[l];
  19. tree[rt].mi = num[l];
  20. return;
  21. }
  22. int mid = (l + r) / 2;
  23. build(rt << 1, l, mid);
  24. build(rt << 1 | 1, mid + 1, r);
  25. tree[rt].ma = max(tree[rt << 1].ma, tree[rt << 1 | 1].ma);
  26. tree[rt].mi = min(tree[rt << 1].mi, tree[rt << 1 | 1].mi);
  27. }
  28. int query_ma(int rt, int l, int r, int L, int R) {
  29. if (L <= l && R >= r) return tree[rt].ma;
  30. int mid = (l + r) / 2;
  31. if (R <= mid) return query_ma(rt << 1, l, mid, L, R);
  32. if (L > mid) return query_ma(rt << 1 | 1, mid + 1, r, L, R);
  33. return max(query_ma(rt << 1, l, mid, L, R), query_ma(rt << 1 | 1, mid + 1, r, L, R));
  34. }
  35. int query_mi(int rt, int l, int r, int L, int R) {
  36. if (L <= l && R >= r) return tree[rt].mi;
  37. int mid = (l + r) / 2;
  38. if (R <= mid) return query_mi(rt << 1, l, mid, L, R);
  39. if (L > mid) return query_mi(rt << 1 | 1, mid + 1, r, L, R);
  40. return min(query_mi(rt << 1, l, mid, L, R), query_mi(rt << 1 | 1, mid + 1, r, L, R));
  41. }
  42. int main() {
  43. // freopen("input.txt", "r", stdin);
  44. int n, q;
  45. scanf("%d %d", &n, &q);
  46. for (int i = 1; i <= n; i ++) scanf("%d", &num[i]);
  47. build(1, 1, n);
  48. for (int i = 1; i <= q; i ++) {
  49. int a, b;
  50. scanf("%d %d", &a, &b);
  51. int ma = query_ma(1, 1, n, a, b);
  52. int mi = query_mi(1, 1, n, a, b);
  53. printf("%d\n", ma - mi);
  54. }
  55. return 0;
  56. }

poj 3468

区间更新,区间求和
思路:裸线段树,要用 lazy 标记 add,否则肯定会超时

  1. /*
  2. ID: LinKArftc
  3. PROG: 3468.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. #define N 100010
  12. int n, q;
  13. long long num[N];
  14. struct Node {
  15. long long sum, add;
  16. Node() { sum = 0; add = 0; }
  17. } tree[N << 2];
  18. void build(int rt, int l, int r) {
  19. if (l == r) {
  20. tree[rt].sum = num[l];
  21. return;
  22. }
  23. int mid = (l + r) / 2;
  24. build(rt << 1, l, mid);
  25. build(rt << 1 | 1, mid + 1, r);
  26. tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
  27. }
  28. long long query(int rt, int l, int r, int L, int R) {
  29. if (L <= l && R >= r) return tree[rt].sum + tree[rt].add * (r - l + 1);
  30. if (tree[rt].add != 0) {
  31. tree[rt].sum += tree[rt].add * (r - l + 1);
  32. tree[rt << 1].add += tree[rt].add;
  33. tree[rt << 1 | 1].add += tree[rt].add;
  34. tree[rt].add = 0;
  35. }
  36. int mid = (l + r) >> 1;
  37. if (R <= mid) return query(rt << 1, l, mid, L, R);
  38. if (L > mid) return query(rt << 1 | 1, mid + 1, r, L, R);
  39. return query(rt << 1, l, mid, L, mid) + query(rt << 1 | 1, mid + 1, r, L, R);
  40. }
  41. void update(int rt, int l, int r, int L, int R, long long c) {
  42. if (L <= l && R >= r) {
  43. tree[rt].add += c;
  44. return ;
  45. }
  46. tree[rt].sum += c * (min(R, r) - max(L, l) + 1);
  47. int mid = (l + r) >> 1;
  48. if (R <= mid) {
  49. update(rt << 1, l, mid, L, R, c);
  50. return ;
  51. }
  52. if (L > mid) {
  53. update(rt << 1 | 1, mid + 1, r, L, R, c);
  54. return ;
  55. }
  56. update(rt << 1, l, mid, L, mid, c);
  57. update(rt << 1 | 1, mid + 1, r, L, R, c);
  58. return ;
  59. }
  60. int main() {
  61. // freopen("input.txt", "r", stdin);
  62. while (~scanf("%d %d", &n, &q)) {
  63. for (int i = 1; i <= n; i ++) scanf("%lld", &num[i]);
  64. build(1, 1, n);
  65. char sel;
  66. int a, b;
  67. long long c;
  68. for (int i = 1; i <= q; i ++) {
  69. scanf(" %c", &sel);
  70. if (sel == 'Q') {
  71. scanf("%d %d", &a, &b);
  72. printf("%lld\n", query(1, 1, n, a, b));
  73. } else {
  74. scanf("%d %d %lld", &a, &b, &c);
  75. update(1, 1, n, a, b, c);
  76. }
  77. }
  78. }
  79. return 0;
  80. }

poj 2991

题意: 起重机的机械臂, 由n段组成, 对某一些连接点进行旋转, 询问每次操作后的末端坐标.
思路:这一题相当恶心。。。因为后面的杆子是跟着动的,所以他们整体上的变化的角度都是一样的,这就变成了区间更新的线段树了,查询很简单,就是顶点的坐标。
线段树节点保存该区间(即第 i 到第 j 条杆子的整体,从起点到终点)的一个向量,存了 x 和 y 坐标(这是向量,不是实际在坐标系中的坐标!)。
add 数组就是 lazy 标记。因为一个向量 (x, y) 逆时针转角度 a 以后,向量变成(cos(a) * x - sin(a) * y, sin(a) * x + cos(a) * y),所以,我们在 add 中记录当前区间逆时针转的角度。
用一个 angle 数组存相邻线段之间的角度,通过这个值和到时候要修改成的角度a来换算出逆时针转过的角度

  1. /*
  2. ID: LinKArftc
  3. PROG: 2991.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. #include <cmath>
  11. const double pi = acos(-1.0);
  12. const double eps = 1e-9;
  13. const int maxn = 10010;
  14. struct Node {
  15. double x, y;
  16. int add;
  17. Node(int _x, int _y, int _add) : x(_x), y(_y), add(_add) {}
  18. Node() { add = 0; }
  19. } tree[maxn << 2];
  20. int angle[maxn];
  21. void rotate(int rt, int a) {
  22. double x = tree[rt].x;
  23. double y = tree[rt].y;
  24. double ang = pi / 180 * a;
  25. tree[rt].x = cos(ang) * x - sin(ang) * y;
  26. tree[rt].y = sin(ang) * x + cos(ang) * y;
  27. }
  28. void build(int rt, int l, int r) {
  29. if (l == r) {
  30. scanf("%lf", &tree[rt].y);
  31. tree[rt].x = 0;
  32. return;
  33. }
  34. int mid = (l + r) >> 1;
  35. build(rt << 1, l, mid);
  36. build(rt << 1 | 1, mid + 1, r);
  37. tree[rt].x = tree[rt << 1].x + tree[rt << 1 | 1].x;
  38. tree[rt].y = tree[rt << 1].y + tree[rt << 1 | 1].y;
  39. }
  40. void update(int rt, int l, int r, int L, int R, int a) {
  41. if(L <= l && R >= r) {
  42. tree[rt].add += a;
  43. rotate(rt, a);
  44. return;
  45. }
  46. if(tree[rt].add) {
  47. rotate(rt << 1, tree[rt].add);
  48. rotate(rt << 1 | 1, tree[rt].add);
  49. tree[rt << 1].add += tree[rt].add;
  50. tree[rt << 1 | 1].add += tree[rt].add;
  51. tree[rt].add = 0;
  52. }
  53. int mid = (r + l) >> 1;
  54. if (L <= mid) update(rt << 1, l, mid, L, R, a);
  55. if (R > mid) update(rt << 1 | 1, mid + 1, r, L, R, a);
  56. tree[rt].x = tree[rt << 1].x + tree[rt << 1 | 1].x;
  57. tree[rt].y = tree[rt << 1].y + tree[rt << 1 | 1].y;
  58. }
  59. int main() {
  60. // freopen("input.txt", "r", stdin);
  61. int n, m;
  62. int cnt = 0;
  63. while (~scanf("%d%d", &n, &m)) {
  64. memset(tree, 0, sizeof(tree));
  65. if(cnt ++) printf("\n");
  66. build(1, 1, n);
  67. for (int i = 1; i <= n; i ++) angle[i] = 180;
  68. while (m --) {
  69. int s, a;
  70. scanf("%d%d",&s, &a);
  71. int x = (a - angle[s] + 360) % 360;
  72. update(1, 1, n, s + 1, n, x);
  73. angle[s] = a;
  74. printf("%.2lf %.2lf\n",tree[1].x + eps, tree[1].y + eps);
  75. }
  76. }
  77. return 0;
  78. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注