[关闭]
@morehigh 2017-02-25T03:10:11.000000Z 字数 6567 阅读 923

CQUPT 集训队专题训练(线段树)

线段树


【创建线段树(初始化)】:
在完全二叉树中假如一个结点的序号(数组下标)为 I ,那么 (二叉树基本关系)
I 的父亲为 I/2,
I 的另一个兄弟为 I/2*2 或 I/2*2+1
I 的两个孩子为 I*2 (左) I*2+1(右)

区间更新:
lazy操作
void pushdown(int num)
{
if(laz[num]!=0)
{
tre[num*2]+=laz[num];
tre[num*2+1]+=laz[num];
laz[num*2]+=laz[num];
laz[num*2+1]+=laz[num];
laz[num]=0;
}
}
必须在更新完儿子节点的值之后再反过来更新父亲节点。

A - 敌兵布阵
题意:

  1. N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人
  2. (1) Add i j,ij为正整数,表示第i个营地增加j个人(j不超过30
  3. (2)Sub i j ,ij为正整数,表示第i个营地减少j个人(j不超过30);
  4. (3)Query i j ,ij为正整数,i<=j,表示询问第i到第j个营地的总人数;
  5. (4)End 表示结束,这条命令在每组数据最后出现;
  6. 每组数据最多有40000条命令

解题思路:

  1. 线段树,单点更新,维护区间和问题

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <cmath>
  6. using namespace std;
  7. struct Node
  8. {
  9. int a,b,sum;
  10. }t[150000];
  11. int r[50010],SUM;
  12. void make(int x,int y,int num)
  13. {
  14. t[num].a=x;
  15. t[num].b=y;
  16. if(x==y)
  17. t[num].sum=r[y];
  18. else
  19. {
  20. make(x,(x+y)/2,num*2);
  21. make((x+y)/2+1,y,num*2+1);
  22. t[num].sum=t[2*num].sum+t[2*num+1].sum;
  23. }
  24. }
  25. void query(int x,int y,int num)
  26. {
  27. if(x<=t[num].a&&y>=t[num].b)
  28. SUM+=t[num].sum;
  29. else
  30. {
  31. int adv=(t[num].a+t[num].b)/2;
  32. if(x>adv) query(x,y,num*2+1);
  33. else if(y<=adv) query(x,y,num*2);
  34. else
  35. {
  36. query(x,y,2*num);
  37. query(x,y,num*2+1);
  38. }
  39. }
  40. }
  41. void add(int x,int y,int num)
  42. {
  43. t[num].sum+=y;
  44. if(t[num].a==x&&t[num].b==x)return;
  45. if(x>(t[num].a+t[num].b)/2)
  46. add(x,y,2*num+1);
  47. else
  48. add(x,y,2*num);
  49. }
  50. void sub(int x,int y,int num)
  51. {
  52. t[num].sum-=y;
  53. if(t[num].a==x&&t[num].b==x)return;
  54. if(x>(t[num].a+t[num].b)/2)
  55. sub(x,y,2*num+1);
  56. else
  57. sub(x,y,2*num);
  58. }
  59. int main()
  60. {
  61. int t,n;
  62. char com[10];
  63. cin>>t;
  64. int j=1;
  65. while(t--)
  66. {
  67. int temp,a,b;
  68. scanf("%d",&n);
  69. r[0]=0;
  70. for(int i=1;i<=n;i++)
  71. scanf("%d",&r[i]);
  72. make(1,n,1);
  73. cout<<"Case "<<j++<<":"<<endl;
  74. while(cin>>com)
  75. {
  76. if(strcmp(com,"End")==0)
  77. break;
  78. if(strcmp(com,"Query")==0)
  79. {
  80. scanf("%d%d",&a,&b);
  81. SUM=0;
  82. query(a,b,1);
  83. cout<<SUM<<endl;
  84. }
  85. if(strcmp(com,"Add")==0)
  86. {
  87. scanf("%d%d",&a,&b);
  88. add(a,b,1);
  89. }
  90. if(strcmp(com,"Sub")==0)
  91. {
  92. scanf("%d%d",&a,&b);
  93. sub(a,b,1);
  94. }
  95. }
  96. }
  97. return 0;
  98. }

B - Color the ball
题意:

  1. N个气球排成一排,接下来N行每行进行一次涂色操作,输入a,b,从气球a开始到气球b依次给每个气球涂一次颜色。求每个气球被涂了几次颜色?

解题思路:

  1. 每次操作更新区间涂色次数,最后将路径区间求和,得到涂色次数

代码:

  1. 树状数组代码
  2. #include <iostream>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <algorithm>
  6. using namespace std;
  7. int tree[500003];
  8. int lowbit(int x)
  9. {
  10. return x&-x;
  11. }
  12. int update(int x,int val)
  13. {
  14. while(x>0)
  15. {
  16. tree[x]+=val;
  17. x-=lowbit(x);
  18. }
  19. }
  20. int query(int x,int n)
  21. {
  22. int ans=0;
  23. while(x<=n)
  24. {
  25. ans+=tree[x];
  26. x+=lowbit(x);
  27. }
  28. return ans;
  29. }
  30. int main()
  31. {
  32. int n,a,b;
  33. while(scanf("%d",&n)!=EOF&&n)
  34. {
  35. memset(tree,0,sizeof(tree));
  36. for(int i=1;i<=n;i++)
  37. {
  38. scanf("%d%d",&a,&b);
  39. update(b,1);
  40. update(a-1,-1);
  41. }
  42. for(int i=1;i<=n;i++)
  43. {
  44. if(i!=n)
  45. cout<<query(i,n)<<" ";
  46. else
  47. cout<<query(i,n)<<endl;
  48. }
  49. }
  50. return 0;
  51. }
  52. 线段树代码
  53. #include <iostream>
  54. #include <cstdio>
  55. #include <cstring>
  56. #include <algorithm>
  57. using namespace std;
  58. struct Node
  59. {
  60. int l,r,num;
  61. }tree[1000003];
  62. int ans[1000000];
  63. void build(int l,int r,int i)
  64. {
  65. tree[i].l=l;
  66. tree[i].r=r;
  67. tree[i].num=0;
  68. if(l!=r){
  69. int mid=(l+r)/2;
  70. build(l,mid,2*i);
  71. build(mid+1,r,2*i+1);
  72. }
  73. }
  74. void query(int l,int r,int i)
  75. {
  76. if(l==tree[i].l&&r==tree[i].r)
  77. tree[i].num+=1;
  78. else
  79. {
  80. int mid=(tree[i].l+tree[i].r)/2;
  81. if(r<=mid)
  82. query(l,r,2*i);
  83. else if(l>mid)
  84. query(l,r,2*i+1);
  85. else
  86. {
  87. query(l,mid,2*i);
  88. query(mid+1,r,2*i+1);
  89. }
  90. }
  91. }
  92. void solve(int x)
  93. {
  94. for(int i=tree[x].l;i<=tree[x].r;i++)
  95. ans[i]+=tree[x].num;
  96. if(tree[x].l==tree[x].r)
  97. return;
  98. solve(2*x+1);
  99. solve(2*x);
  100. }
  101. int main()
  102. {
  103. int n,x,y;
  104. while(scanf("%d",&n)!=EOF&&n)
  105. {
  106. build(1,n,1);
  107. for(int i=1;i<=n;i++)
  108. {
  109. scanf("%d%d",&x,&y);
  110. query(x,y,1);
  111. }
  112. memset(ans,0,sizeof(ans));
  113. solve(1);
  114. for(int i=1;i<=n;i++)
  115. {
  116. if(i!=n)
  117. cout<<ans[i]<<" ";
  118. else
  119. cout<<ans[i]<<endl;
  120. }
  121. }
  122. return 0;
  123. }

D - Tunnel Warfare
题意:

  1. n个村庄,m个事件,三种操作
  2. 1.D x:第x个村庄被摧毁
  3. 2.Q x:输出第x个村庄可以直接和间接相连的村庄个数
  4. 3.R:最后一个被摧毁的村庄重建

解题思路:

  1. 对于一个点,看它是在当前区间的左半还是右半。在左半的话,看看是不是在右端的连续区间内,是的话,还要加上右半区间的左端连续区间。否则的话,只要计算它在左半区间的连接情况即可,在右半的话同理,看看是不是在左端的连续区间内,是的话,还要加上左半区间的右端连续区间。否则的话,只要计算它在右半区间的连接情况即可

代码:

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

E - A Simple Problem with Integers
题意:

  1. N个数字,进行Q次操作。(1 N,Q 100000 两种操作方法:
  2. "C a b c",从第ab个数字都加上c
  3. "Q a b",查询从ab所有数字之和

解题思路:

  1. 线段树的区间更新问题。
  2. 在区间更新的时候,为了节约时间,便利到左右儿子的区间在(x,y)之间的话,暂时不做修改,但是要把这个修改动作存在laz[num]里,下次需要的时候再修改。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. #define ll long long
  7. #define CL(a,b) memset(a,b,sizeof(a))
  8. #define MAXN 100010
  9. struct node
  10. {
  11. int l,r;
  12. ll s,add;//add为每次加的数
  13. }t[MAXN*4];
  14. int hh[MAXN];
  15. int n,q;
  16. ll ans;
  17. void build(int l, int r, int i)
  18. {
  19. t[i].l = l;
  20. t[i].r = r;
  21. t[i].add = 0;
  22. if(l == r) return ;
  23. int mid = (l+r)/2;
  24. build(l, mid, i*2);
  25. build(mid+1, r, i*2+1);
  26. t[i].s = t[i*2].s+t[i*2+1].s;
  27. }
  28. void update(int l, int r, int add, int i)
  29. {
  30. if(t[i].l>r || t[i].r<l) return ;
  31. if(t[i].l>=l && t[i].r<=r)
  32. {
  33. t[i].s += (t[i].r-t[i].l+1)*add;
  34. t[i].add += add;
  35. return ;
  36. }
  37. if(t[i].add)
  38. {
  39. t[i*2].s += (t[i*2].r-t[i*2].l+1)*t[i].add;
  40. t[i*2].add += t[i].add;
  41. t[i*2+1].s += (t[i*2+1].r-t[i*2+1].l+1)*t[i].add;
  42. t[i*2+1].add += t[i].add;
  43. t[i].add = 0;
  44. }
  45. update(l, r, add, i<<1);
  46. update(l, r, add, i<<1|1);
  47. t[i].s = t[i*2].s+t[i*2+1].s;
  48. }
  49. void query(int l, int r, int i)
  50. {
  51. if(t[i].l>r || t[i].r<l) return ;
  52. if(t[i].l>=l && t[i].r<=r)
  53. {
  54. ans += t[i].s;
  55. return ;
  56. }
  57. if(t[i].add)
  58. {
  59. t[i*2].s += (t[i*2].r-t[i*2].l+1)*t[i].add;
  60. t[i*2].add += t[i].add;
  61. t[i*2+1].s += (t[i*2+1].r-t[i*2+1].l+1)*t[i].add;
  62. t[i*2+1].add += t[i].add;
  63. t[i].add = 0;
  64. }
  65. query(l, r, i*2);
  66. query(l, r, i*2+1);
  67. t[i].s = t[i*2].s+t[i*2+1].s;
  68. }
  69. int main()
  70. {
  71. int a,b,c;
  72. ll k;
  73. char ch;
  74. while(scanf("%d%d",&n,&q)==2)
  75. {
  76. for(int i=1; i<=n; i++)
  77. scanf("%d",&hh[i]);
  78. build(1, n, 1);
  79. for(int i=1; i<=n; i++)
  80. update(i, i, hh[i], 1);
  81. while(q--)
  82. {
  83. getchar();
  84. scanf("%c",&ch);
  85. if(ch == 'C')
  86. {
  87. scanf("%d%d%d",&a,&b,&c);
  88. update(a, b, c, 1);
  89. }
  90. if(ch == 'Q')
  91. {
  92. ans = 0;
  93. scanf("%d%d",&a,&b);
  94. query(a, b, 1);
  95. cout<<ans<<endl;
  96. }
  97. }
  98. }
  99. return 0;
  100. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注