[关闭]
@sensitive-cs 2017-03-30T12:33:10.000000Z 字数 8596 阅读 806

CQUPT 线段树

题解


HDU 1166:敌兵布阵
题意:
给出n个数,有3种操作,一种是对某个数加上一个数,一种是对某个数减去一个数,一种是查询一个区间内的数的和。
思路:
线段树裸题,主要是点更新,也可以用树状数组做。(巨坑的是位运算的时候以为除以2,就写的2,导致wa了几发,引以为戒。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int a[50005];
  4. int sum[200005];
  5. void pushup(int rt)
  6. {
  7. sum[rt] = sum[rt<<1] + sum[rt << 1|1];
  8. }
  9. void build(int l,int r,int rt)
  10. {
  11. if (l == r)
  12. {
  13. sum[rt] = a[l];
  14. return;
  15. }
  16. int m = (l+r) >> 1;
  17. build(l,m,rt << 1);
  18. build(m+1,r,rt << 1|1);
  19. pushup(rt);
  20. }
  21. void update(int ll,int c,int l,int r,int rt)
  22. {
  23. if (l == r)
  24. {
  25. sum[rt] += c;
  26. return;
  27. }
  28. int m = (l+r) >> 1;
  29. if (ll <= m) update(ll,c,l,m,rt << 1);
  30. else update(ll,c,m+1,r,rt << 1|1);
  31. pushup(rt);
  32. }
  33. int query(int ll,int rr,int l,int r,int rt)
  34. {
  35. if (ll <= l && r <= rr)
  36. {
  37. return sum[rt];
  38. }
  39. int m = (l+r) >> 1;
  40. int ans = 0;
  41. if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
  42. if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
  43. return ans;
  44. }
  45. int main()
  46. {
  47. int t;
  48. scanf("%d",&t);
  49. for (int o = 1;o <= t;o++)
  50. {
  51. int n;
  52. scanf("%d",&n);
  53. for (int i = 1;i <= n;i++)
  54. scanf("%d",&a[i]);
  55. build(1,n,1);
  56. char ch[20];
  57. printf("Case %d:\n",o);
  58. while (scanf("%s",ch) != EOF)
  59. {
  60. if (ch[0] == 'E') break;
  61. int x,y;
  62. scanf("%d%d",&x,&y);
  63. if (ch[0] == 'S')
  64. {
  65. update(x,-y,1,n,1);
  66. }
  67. if (ch[0] == 'A')
  68. {
  69. update(x,y,1,n,1);
  70. }
  71. if (ch[0] == 'Q')
  72. {
  73. int ans = 0;
  74. ans = query(x,y,1,n,1);
  75. printf("%d\n",ans);
  76. }
  77. }
  78. }
  79. return 0;
  80. }

poj 3468:A SIMPLE PROBLEM WITH INTEGERS
题意:
给出n个数字,两种操作,一种是给给定区间内的每一个数加上一个数,另一种是查询某个区间内的数字之和。
思路:
纯线段树裸题,直接套模板。不过要注意先建树(又差点卡死2333,再进行操作。
代码:

  1. #include <string.h>
  2. #include <stdio.h>
  3. int a[100005];
  4. long long add[400005];
  5. long long sum[400005];
  6. void pushup(int rt)
  7. {
  8. sum[rt] = sum[rt << 1] + sum[rt << 1|1];
  9. }
  10. void build(int l,int r,int rt)
  11. {
  12. if (l == r)
  13. {
  14. sum[rt] = a[l];
  15. return;
  16. }
  17. int m = (l+r) >> 1;
  18. build(l,m,rt << 1);
  19. build(m+1,r,rt << 1|1);
  20. pushup(rt);
  21. }
  22. void pushdown(int rt,int ln,int rn)
  23. {
  24. if (add[rt])
  25. {
  26. add[rt << 1] += add[rt];
  27. add[rt << 1|1] += add[rt];
  28. sum[rt << 1] += ln * add[rt];
  29. sum[rt << 1|1] += rn *add[rt];
  30. add[rt] = 0;
  31. }
  32. }
  33. void update(int ll,int rr,int c,int l,int r,int rt)
  34. {
  35. if (ll <= l && r <= rr)
  36. {
  37. sum[rt] += c*(r-l+1);
  38. add[rt] += c;
  39. return;
  40. }
  41. int m = (l+r) >> 1;
  42. pushdown(rt,m-l+1,r-m);
  43. if (ll <= m) update(ll,rr,c,l,m,rt << 1);
  44. if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);
  45. pushup(rt);
  46. }
  47. long long query(int ll,int rr,int l,int r,int rt)
  48. {
  49. if (ll <= l && r <= rr)
  50. {
  51. return sum[rt];
  52. }
  53. int m = (l+r) >> 1;
  54. pushdown(rt,m-l+1,r-m);
  55. long long ans = 0;
  56. if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
  57. if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
  58. return ans;
  59. }
  60. int main()
  61. {
  62. int n,q;
  63. scanf("%d%d",&n,&q);
  64. memset(a,0,sizeof(a));
  65. memset(add,0,sizeof(add));
  66. memset(sum,0,sizeof(sum));
  67. for (int i = 1;i <= n;i++)
  68. scanf("%d",&a[i]);
  69. build(1,n,1);
  70. for (int i = 1;i <= q;i++)
  71. {
  72. char s[10];
  73. scanf("%s",s);
  74. int x,y,z;
  75. if (s[0] == 'Q')
  76. {
  77. scanf("%d%d",&x,&y);
  78. long long res = query(x,y,1,n,1);
  79. printf("%I64d\n",res);
  80. }
  81. else
  82. {
  83. scanf("%d%d%d",&x,&y,&z);
  84. update(x,y,z,1,n,1);
  85. }
  86. }
  87. return 0;
  88. }

HDU 1556:COLOR THE BALL
题意:
给出n个区间,每次对1个区间进行更新,对区间的每个数都加1,问每个数被更新了多少次。
思路:
线段树裸题,区间更新,单点查询,也可以用区间查询,左右端点相同就可以了。
代码:

  1. #include <string.h>
  2. #include <stdio.h>
  3. int a[100005];
  4. int add[400005];
  5. int sum[400005];
  6. void pushup(int rt)
  7. {
  8. sum[rt] = sum[rt << 1] + sum[rt << 1|1];
  9. }
  10. void build(int l,int r,int rt)
  11. {
  12. if (l == r)
  13. {
  14. sum[rt] = a[l];
  15. return;
  16. }
  17. int m = (l+r) >> 1;
  18. build(l,m,rt << 1);
  19. build(m+1,r,rt << 1|1);
  20. pushup(rt);
  21. }
  22. void pushdown(int rt,int ln,int rn)
  23. {
  24. if (add[rt])
  25. {
  26. add[rt << 1] += add[rt];
  27. add[rt << 1|1] += add[rt];
  28. sum[rt << 1] += ln * add[rt];
  29. sum[rt << 1|1] += rn *add[rt];
  30. add[rt] = 0;
  31. }
  32. }
  33. void update(int ll,int rr,int c,int l,int r,int rt)
  34. {
  35. if (ll <= l && r <= rr)
  36. {
  37. sum[rt] += c*(r-l+1);
  38. add[rt] += c;
  39. return;
  40. }
  41. int m = (l+r) >> 1;
  42. pushdown(rt,m-l+1,r-m);
  43. if (ll <= m) update(ll,rr,c,l,m,rt << 1);
  44. if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);
  45. pushup(rt);
  46. }
  47. long long query(int ll,int rr,int l,int r,int rt)
  48. {
  49. if (ll <= l && r <= rr)
  50. {
  51. return sum[rt];
  52. }
  53. int m = (l+r) >> 1;
  54. pushdown(rt,m-l+1,r-m);
  55. long long ans = 0;
  56. if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
  57. if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
  58. return ans;
  59. }
  60. int main()
  61. {
  62. int n;
  63. while (scanf("%d",&n) == 1&& n)
  64. {
  65. memset(a,0,sizeof(a));
  66. memset(add,0,sizeof(add));
  67. memset(sum,0,sizeof(sum));
  68. build(1,n,1);
  69. for (int i = 1;i <= n;i++)
  70. {
  71. int a,b;
  72. scanf("%d%d",&a,&b);
  73. update(a,b,1,1,n,1);
  74. }
  75. for (int i = 1;i <= n;i++)
  76. {
  77. int res = query(i,i,1,n,1);
  78. if (i == 1) printf("%d",res);
  79. else printf(" %d",res);
  80. }
  81. printf("\n");
  82. }
  83. return 0;
  84. }

HDU1540:tunnel warfare
题意:
有n个村庄,他们是连着的,由于战争,有些村庄被毁了,被毁了之后又恢复。现在有3种指令,一种是毁灭村庄,另一种是回复村庄,还有一种是查询一个村庄连着的村庄有多少个。
思路1:
由于是跟区间有关,求区间的最大长度,所以说用线段树的区间合并,注释在代码中。
思路2:
看了一篇博客,博主天纵奇才,想到只用线段树的单点更新。
比如现在有1 2 3 4 5 6 7 8有8个村庄,我回去2和7,查询4的时候就是7-2-1。
博主想到最大值树和最小值树,最大值树就用一个很大的之来初始化,最小值数就用-1初始化,每次毁掉村庄,就用这个村庄的端点去覆盖-1或者最大值。当查询的时候,就用查询点左边的最大值,和查询点右边的最小值,进行如7-2-1这样的操作就行了。
注意防止越界的方法,把0点赋值为-1,n+1点赋值为无穷大就行了。
代码1:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using namespace std;
  5. const int cnt = 50010;
  6. struct ss
  7. {
  8. int l,r;
  9. int ls,rs,ms;
  10. }node[4*cnt];
  11. int st[cnt];
  12. void build(int l,int r,int rt)
  13. {
  14. node[rt].l = l,node[rt].r = r;
  15. node[rt].ls = node[rt].rs = node[rt].ms = r - l + 1;
  16. if (l != r)
  17. {
  18. int mid = (l+r) >> 1;
  19. build(l,mid,rt << 1);
  20. build(mid+1,r,rt << 1|1);
  21. }
  22. return;
  23. }
  24. void update(int rt,int x,int flag)
  25. {
  26. if (node[rt].l == node[rt].r)
  27. {
  28. node[rt].ls = node[rt].ms = node[rt].rs = flag;
  29. return;
  30. }
  31. int mid = (node[rt].l + node[rt].r) >> 1;
  32. if (x <= mid) update(rt << 1,x,flag);
  33. else update(rt << 1|1,x,flag);
  34. node[rt].ls = node[rt << 1].ls;
  35. node[rt].rs = node[rt << 1|1].rs;
  36. if (node[rt << 1].ms == node[rt << 1].r - node[rt << 1].l + 1)
  37. node[rt].ls += node[rt << 1|1].ls;
  38. if (node[rt << 1|1].ms == node[rt << 1|1].r - node[rt << 1|1].l+1)
  39. node[rt].rs += node[rt << 1].rs;
  40. node[rt].ms = max(max(node[rt<<1].ms,node[rt << 1|1].ms),node[rt << 1].rs + node[rt << 1|1].ls);
  41. }
  42. int query(int rt,int x)
  43. {
  44. if (node[rt].r == node[rt].l || node[rt].ms == 0 || node[rt].ms == node[rt].r - node[rt].l + 1)
  45. {
  46. return node[rt].ms;
  47. }
  48. int mid = (node[rt].l + node[rt].r) >> 1;
  49. if (x <= mid)
  50. {
  51. if (x < node[rt << 1].r - node[rt << 1].rs + 1)
  52. return query(rt << 1,x);
  53. else
  54. return node[rt << 1].rs + node[rt << 1|1].ls;
  55. }
  56. else
  57. {
  58. if (x > node[rt << 1|1].l + node[rt << 1|1].ls - 1)
  59. return query(rt << 1|1,x);
  60. else
  61. return node[rt << 1].rs + node[rt << 1|1].ls;
  62. }
  63. }
  64. int main()
  65. {
  66. int n,m;
  67. while (scanf("%d%d",&n,&m) != EOF)
  68. {
  69. memset(st,0,sizeof(st));
  70. build(1,n,1);
  71. int top = 0;
  72. for (int i = 0;i < m;i++)
  73. {
  74. char t[5];
  75. scanf("%s",t);
  76. if (t[0] == 'D')
  77. {
  78. int x;
  79. scanf("%d",&x);
  80. update(1,x,0);
  81. st[top++] = x;
  82. }
  83. if (t[0] == 'R')
  84. {
  85. int x = st[--top];
  86. update(1,x,1);
  87. }
  88. if (t[0] == 'Q')
  89. {
  90. int x;
  91. scanf("%d",&x);
  92. int sum = query(1,x);
  93. printf("%d\n",sum);
  94. }
  95. }
  96. }
  97. return 0;
  98. }

代码2

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using namespace std;
  5. const int inf = 0x3f3f3f3f;
  6. const int cnt = 5e4+10;
  7. int ma[cnt << 2];
  8. int mi[cnt << 2];
  9. int st[cnt];
  10. int g = inf,v = -1;
  11. int m,n;
  12. void update(int rt,int x,int flag,int l,int r)
  13. {
  14. if (l == r)
  15. {
  16. if (flag)
  17. {
  18. ma[rt] = -1;
  19. mi[rt] = inf;
  20. }
  21. else
  22. mi[rt] = ma[rt] = x;
  23. return;
  24. }
  25. int mid = (l+r) >> 1;
  26. if (x <= mid) update(rt << 1,x,flag,l,mid);
  27. else update(rt << 1|1,x,flag,mid+1,r);
  28. ma[rt] = max(ma[rt << 1],ma[rt << 1|1]);
  29. mi[rt] = min(mi[rt << 1],mi[rt << 1|1]);
  30. return;
  31. }
  32. void query1(int rt,int l,int r,int L,int R)
  33. {
  34. if (l >= L && r <= R)
  35. {
  36. g = min(g,mi[rt]);
  37. return;
  38. }
  39. int mid = (l+r) >> 1;
  40. if (L <= mid) query1(rt << 1,l,mid,L,R);
  41. if (R > mid) query1(rt << 1|1,mid+1,r,L,R);
  42. }
  43. void query2(int rt,int l,int r,int L,int R)
  44. {
  45. if (l >= L && r <= R)
  46. {
  47. v = max(v,ma[rt]);
  48. return;
  49. }
  50. int mid = (l+r) >> 1;
  51. if (L <= mid) query2(rt << 1,l,mid,L,R);
  52. if (R > mid) query2(rt << 1|1,mid+1,r,L,R);
  53. }
  54. int main()
  55. {
  56. while (scanf("%d%d",&n,&m) != EOF)
  57. {
  58. memset(ma,-1,sizeof(ma));
  59. memset(mi,inf,sizeof(mi));
  60. memset(st,0,sizeof(st));
  61. int top = 0;
  62. update(1,0,0,0,n+1);
  63. update(1,n+1,0,0,n+1);
  64. for (int i = 0;i < m;i++)
  65. {
  66. char s[5];
  67. scanf("%s",s);
  68. if (s[0] == 'D')
  69. {
  70. int x;
  71. scanf("%d",&x);
  72. st[top++] = x;
  73. update(1,x,0,0,n+1);
  74. }
  75. if (s[0] == 'R')
  76. {
  77. int x = st[--top];
  78. update(1,x,1,0,n+1);
  79. }
  80. if (s[0] == 'Q')
  81. {
  82. int x;
  83. scanf("%d",&x);
  84. g = inf;
  85. v = -1;
  86. query1(1,0,n+1,x,n+1);
  87. query2(1,0,n+1,0,x);
  88. if (g == v)
  89. printf("0\n");
  90. else
  91. printf("%d\n",g - v - 1);
  92. }
  93. }
  94. }
  95. return 0;
  96. }

poj2528:mayor's posters
题意:
相当于区间染色问题,有一面墙,开始时没有颜色,给出n个操作,每一次染一种颜色,每次染的颜色都不同,问最后墙上有多少颜色。
思路:
一开始想到了是线段树,但是奈何区间太大,1000万的数量级。后来搜题解报告知道了是离散化,所以就浅浅的学习了一下。离散化,对于这题,就是把大的区间映射到小的区间以减小复杂度。但是这题的离散化比较特殊,需要在不连续的坐标之间随意添加一个中间小的数,才能够保证不出错,这一点可以自己举例子理解。最开始写线段树,忘了,就偷懒写了简化版的线段树,果断tle,后来再次按照模板写,wa了,再一看,居然是pushdown在query的时候也需要下方,唉,还是不要懒,more haste,less speed。谨记!!!

代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <map>
  4. #include <vector>
  5. #include <algorithm>
  6. using namespace std;
  7. bool cx[10000005];
  8. vector<int> xl;
  9. int x[10005],y[10005];
  10. int a[10000005];
  11. int ans[10005];
  12. int b[200005],lazy[200005];
  13. void pushdown(int rt)
  14. {
  15. if (lazy[rt] != -1)
  16. {
  17. lazy[rt << 1] = lazy[rt << 1|1] = lazy[rt];
  18. b[rt << 1] = b[rt << 1|1] = b[rt] = lazy[rt];
  19. lazy[rt] = -1;
  20. }
  21. }
  22. void update(int l,int r,int k,int ll,int rr,int rt)
  23. {
  24. //printf("***\n");
  25. if (l >= ll && r <= rr)
  26. {
  27. b[rt] = k;
  28. lazy[rt] = k;
  29. return;
  30. }
  31. else
  32. {
  33. pushdown(rt);
  34. int mid = (l + r) >> 1;
  35. if (ll <= mid) update(l,mid,k,ll,rr,rt << 1);
  36. if (rr > mid) update(mid+1,r,k,ll,rr,rt << 1|1);
  37. }
  38. }
  39. void query(int l,int r,int rt)
  40. {
  41. if (l == r)
  42. {
  43. ans[b[rt]]++;
  44. return;
  45. }
  46. pushdown(rt);
  47. int mid = (l+r) >> 1;
  48. query(l,mid,rt << 1);
  49. query(mid+1,r,rt << 1|1);
  50. }
  51. int main()
  52. {
  53. int t;
  54. scanf("%d",&t);
  55. while (t--)
  56. {
  57. int n;
  58. memset(x,0,sizeof(x));
  59. memset(y,0,sizeof(y));
  60. memset(a,0,sizeof(a));
  61. memset(ans,0,sizeof(ans));
  62. memset(cx,0,sizeof(cx));
  63. memset(b,0,sizeof(b));
  64. memset(lazy,-1,sizeof(lazy));
  65. scanf("%d",&n);
  66. xl.clear();
  67. for (int i = 1;i <= n;i++)
  68. {
  69. scanf("%d%d",&x[i],&y[i]);
  70. if (!cx[x[i]])
  71. {
  72. cx[x[i]] = 1;
  73. xl.push_back(x[i]);
  74. }
  75. if (!cx[y[i]])
  76. {
  77. cx[y[i]] = 1;
  78. xl.push_back(y[i]);
  79. }
  80. }
  81. sort(xl.begin(),xl.end());
  82. int len = xl.size();
  83. for (int i = 1;i < len;++i)
  84. {
  85. if (xl[i] - xl[i-1] > 1)
  86. xl.push_back(xl[i] - 1);
  87. }
  88. sort(xl.begin(),xl.end());
  89. len = xl.size();
  90. for (int i = 0;i < len;i++)
  91. {
  92. a[xl[i]] = i+1;
  93. }
  94. for (int i = 1;i <= n;i++)
  95. {
  96. x[i] = a[x[i]];
  97. y[i] = a[y[i]];
  98. }
  99. for (int i = 1;i <= n;i++)
  100. {
  101. update(1,len,i,x[i],y[i],1);
  102. }
  103. int sum = 0;
  104. query(1,len,1);
  105. for (int i = 1;i <= n;i++)
  106. {
  107. if (ans[i]) sum++;
  108. }
  109. printf("%d\n",sum);
  110. }
  111. return 0;
  112. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注