Loading

Different Integers(树状数组)

描述

传送门:我是传送门

Given a sequence of integers a1,a2,,ana1,a2,…,an and qq pairs of integers (l1,r1),(l2,r2),,(lq,rq)(l1,r1),(l2,r2),…,(lq,rq), find count(l1,r1),count(l2,r2),,count(lq,rq)count(l1,r1),count(l2,r2),…,count(lq,rq)where count(i,j)count(i,j) is the number of different integers among a1,a2,,ai,aj,,ana1,a2,…,ai,aj,…,an.

输入

The input consists of several test cases and is terminated by end-of-file. The first line of each test cases contains two integers nn and qq. 

The second line contains n integers a1,a2,,ana1,a2,…,an. The i-th of the following qq lines contains two integers lili and riri.

输出

For each test case, print qq integers which denote the result.

样例

输入

3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3

输出

2
1
3

Note

  • 1n,q1051≤n,q≤105
  • 1ain1≤ai≤n
  • 1li,rin1≤li,ri≤n
  • The number of test cases does not exceed 10.

思路

牛客多校赛第一场的签到题,但是很遗憾当时树状数组不太会用,没能写出来,当时看其他人写的博客也是没能彻底搞懂.

现在尝试在scaufat的题解牛客网暑期ACM多校训练营(第一场)J 题解 的基础上重新捋一下思路,添加一些代码注释

希望能够对树状数组的灵活应用有更深的理解.

我认为整个题目最关键的地方在于这两个地方

  • 维护一个前缀和,pre[i]表示a[1…i]有多少种不同的数字,那么对于a[l…r]的答案就为pre[r] - pre[l-1] + 在a[l…r]和a[1…l-1]同时出现的数字的种类

  • 左端每次右移的时候把对应的数的下一个位置加入到数状数组中

代码

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int N = 2e5+10;
  4 #define clr(a, x) memset(a, x, sizeof(a))
  5 int n,q;
  6 int a[N];
  7 int pre[N];   // pre[i]表示数组前i个数有多少种不同的数
  8 bool vis[N];
  9 int last[N];  // 用来计算nxt数组用的辅助数组,记录每个数上一次出现的位置
 10 int nxt[N];   // 用来维护数组中每个位置的数下一次出现的位置
 11 int ans[N];
 12 
 13 struct node
 14 {
 15     int l,r,id;
 16     bool operator < (const node &b) const   // 将查询按照左端点排序
 17     {
 18         return this->l < b.l;
 19     }
 20 }query[N];
 21 
 22 int bit[N];
 23 int lowbit(int x)
 24 {
 25     return x&(-x);
 26 }
 27 
 28 void update(int x,int y)
 29 {
 30     while(x < N)
 31         bit[x] += y,x += lowbit(x);
 32 }
 33 
 34 int Query(int x)
 35 {
 36     int ret = 0;
 37     while(x)
 38         ret += bit[x],x -= lowbit(x);
 39     return ret;
 40 }
 41 
 42 int Query(int l,int r)
 43 {
 44     return Query(r)-Query(l-1);
 45 }
 46 
 47 int main()
 48 {
 49     while(scanf("%d %d",&n,&q) != EOF)
 50     {
 51         clr(bit,0); clr(vis,0); clr(nxt,0); clr(last,-1);
 52         // 输入的同时在后边复制一份
 53         for(int i = 1;i <= n;i++)
 54         {
 55             scanf("%d",&a[i]);
 56             a[i+n] = a[i];
 57         }
 58         n *= 2;
 59         
 60         // 我认为这个循环是最神奇的地方
 61         // 很强大 一个月前看的时候根本没怎么看懂
 62         pre[0] = 0;
 63         for(int i = 1;i <= n;i++)
 64         {
 65             // 处理前缀和
 66             if(!vis[a[i]])   // 如果没出现过
 67             {
 68                 vis[a[i]] = true;
 69                 pre[i] = pre[i-1]+1;   // 类似dp
 70             }
 71             else   // 如果已经出现过 
 72             {
 73                 pre[i] = pre[i-1];
 74             }
 75             // 处理nxt[]数组   last[]数组做辅助用
 76             // a[i]首次出现时不会执行此语句 【 ~(-1) = 0 】
 77             if(~last[a[i]])   
 78             {
 79                 nxt[last[a[i]]] = i;
 80             }
 81             last[a[i]] = i;
 82         }
 83         
 84         for(int i = 0;i < q;i++)
 85         {
 86             // 因为原来的代码增加query[i].l的值后又交换
 87             // 了query[i].l与query[i].r的值
 88             // 我直接在输入时交换一下l与r的值 然后增加r的值
 89             // 最终的结果是一样的
 90             scanf("%d %d",&query[i].r,&query[i].l);
 91             query[i].r += n/2;
 92             // 因为在输入完成后会对query[]保存到值按照左端点重新排序
 93             // 因此需要用query[i].id来记录一下原来的顺序,即i的值
 94             query[i].id = i;
 95         }
 96         // 按照左端点对query[]进行排序
 97         sort(query,query+q);
 98         // 从最左端的1开始向右扫描
 99         // 每次右移的时候把对应的数的下一个位置加入到数状数组中
100         int now = 1;
101         for(int i = 0;i < q;i++)
102         {
103             while(now < query[i].l)
104             {
105                 if(~nxt[now])
106                 {
107                     update(nxt[now],1);
108                 }
109                 ++now;
110             }
111             ans[query[i].id] = pre[query[i].r]-pre[query[i].l-1]+Query(query[i].l,query[i].r);
112         }
113         for(int i = 0;i < q;i++)
114             printf("%d\n",ans[i]);
115     }
116     return 0;
117 }

 

posted @ 2021-01-20 20:49  Yiduuannng  阅读(112)  评论(0编辑  收藏  举报