[关闭]
@lychee123 2017-08-14T17:16:52.000000Z 字数 1677 阅读 1996

树状数组(离线求区间不同数字个数)

数据结构


树状数组基础知识

树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值。
  1. ///(树状数组模板不需要理解记住)
  2. const int maxn = 1e5 + 5;
  3. int lowbit(int x)
  4. {
  5. return x&(-x);
  6. }
  7. void Add(int pos,int val)///单点修改
  8. {
  9. for(int i=pos;i<maxn;i+=lowbit(i))
  10. c[i]+=val;
  11. }
  12. int Sum(int pos)///从0位置到pos位置的区间和
  13. {
  14. int ret=0;
  15. for(int i=pos;i>0;i-=lowbit(i))
  16. ret+=c[i];
  17. return ret;
  18. }
  19. //sum函数用于求和。给出的数列中前i项的和。若需求某i,j段的和,则用sum[j]-sum[i-1];

power oj-2459:Submissions of online judge

输入
2 //t两组测试数据
3 //序列长度为3
1 1 4 //序列
2 //有2次询问
1 2 //区间1-2有几个数字
2 3 //区间2-3有几个数字
5
1 1 2 1 3
3
1 5
2 4
3 5
输出
1
2
3
2
3

解题思路

树状数组离线求区间不同数字个数
用vector来存查询区间以及查询顺序v[r].push_back(make_pair(l,i)),开一个map(lastpose)来存pose点上一次出现的位置(从查询的右区间开始往左走)将往左走第一次出现的数的值修改为1,再往前走重复出现的修改为0,所以在用树状数组求区间和的时候区间和即为区间不同数的个数。
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=100010;
  4. int t,n,q,l,r;
  5. int a[maxn],c[maxn];///a[]原数组,c[]树状数组
  6. int ans[maxn];///询问的结果
  7. vector<pair<int,int> >v[maxn];
  8. ///树状数组(区间求和)
  9. int lowbit(int x)
  10. {
  11. return x&(-x);
  12. }
  13. void Add(int pos,int val)///单点修改
  14. {
  15. for(int i=pos;i<=n;i+=lowbit(i))
  16. c[i]+=val;
  17. }
  18. int Sum(int pos)///从0位置到pos位置的区间和
  19. {
  20. int ret=0;
  21. for(int i=pos;i>0;i-=lowbit(i))
  22. ret+=c[i];
  23. return ret;
  24. }
  25. int main()
  26. {
  27. scanf("%d",&t);
  28. while(t--)
  29. {
  30. memset(c,0,sizeof(c));
  31. scanf("%d",&n);
  32. for(int i=1;i<=n;i++)
  33. scanf("%d",&a[i]);
  34. for(int i=1;i<=n;i++)
  35. v[i].clear();
  36. scanf("%d",&q);
  37. for(int i=1;i<=q;i++)
  38. {
  39. scanf("%d%d",&l,&r);
  40. v[r].push_back(make_pair(l,i));
  41. }
  42. unordered_map<int,int>lastpose;///pose上一次出现的位置
  43. ///也可以用map,不过unordered_map更快,用数组的话数据太多装不下
  44. for(int i=1;i<=n;i++)
  45. {
  46. if(lastpose[a[i]])///a[i]的上一位存在-1使树状数组中的值为0
  47. Add(lastpose[a[i]],-1);
  48. Add(i,1);///最近的这个数在树状数组里修改为1
  49. lastpose[a[i]]=i;
  50. for(int j=0;j<v[i].size();j++)
  51. {
  52. int L=v[i][j].first;///左区间L
  53. int R=i;
  54. int id=v[i][j].second;///第id次询问
  55. int sum=Sum(R)-Sum(L-1);
  56. ans[id]=sum;
  57. }
  58. }
  59. for(int i=1;i<=q;i++)
  60. printf("%d\n",ans[i]);
  61. }
  62. return 0;
  63. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注