牛客网2018多校第一场J题

链接:https://www.nowcoder.com/acm/contest/139/J
来源:牛客网

题目描述

Given a sequence of integers a 1, a 2, ..., a n and q pairs of integers (l 1, r 1), (l 2, r 2), ..., (l q, r q), find count(l 1, r 1), count(l 2, r 2), ..., count(l q, r q) where count(i, j) is the number of different integers among a 1, a 2, ..., a i, a j, a j + 1, ..., a n.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test cases contains two integers n and q.
The second line contains n integers a
1
, a
2
, ..., a
n
.
The i-th of the following q lines contains two integers l
i
 and r
i
.

输出描述:

For each test case, print q integers which denote the result.
示例1

输入

复制
3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3

输出

复制
2
1
3

备注:

* 1 ≤ n, q ≤ 10
5

* 1 ≤ a
i
 ≤ n
* 1 ≤ l
i
, r
i
 ≤ n
* The number of test cases does not exceed 10.
参考博客https://www.nowcoder.com/discuss/87249

作者:scaufat
链接:https://www.nowcoder.com/discuss/87249
来源:牛客网

首先将原数组扩展为2倍长的,即 a[i+n] = a[i]
对于查询a[1...l]和a[r..n]有多少种不同的数字可以转换为查询 a[r...l+n]有多少种不同的数字
首先考虑维护一个前缀和,pre[i]表示a[1...i]有多少种不同的数字,那么对于a[l...r]的答案就为pre[r] - pre[l-1] + 在a[l...r]和a[1...l-1]同时出现的数字的种类
对于如何求在a[l...r]和a[1...l-1]同时出现的数字的种类,可以考虑使用树状数组维护,树状数组的第i个点表示a[i]是否已经在1...l出现过,对于每个查询只需要查树状数组中l~r的区间和即可
那么我们只要对区间查询进行排序,对于左端每次右移的时候把对应的数的下一个位置加入到数状数组中即可。
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 const int N = 2e5+10;
  7 int n,m;
  8 int a[N];
  9 int c[N];
 10 int vis[N];
 11 int nex[N]; //记录每个a[i]上一次出现的位置
 12 int last[N];//为了记录nex数组所添加的辅助数组用来记录a[i]上一次出现的位置
 13 int pre[N];
 14 int answer[N];
 15 struct node
 16 {
 17     int l,r,id;
 18 }querys[N];
 19 int cmp(node x,node y)
 20 {
 21     return x.l<y.l;
 22 }
 23 int lowbit(int x)
 24 {
 25     return x&(-x);
 26 }
 27 void update(int id,int val)
 28 {
 29     while(id<=n)
 30     {
 31         c[id]+=val;
 32         id+=lowbit(id);
 33     }
 34 }
 35 int query(int id)
 36 {
 37     int sum = 0;
 38     while(id>0)
 39     {
 40         sum+=c[id];
 41         id-=lowbit(id);
 42     }
 43     return sum;
 44 }
 45 int query(int l,int r)
 46 {
 47     return query(r) - query(l-1);
 48 }
 49 int main()
 50 {
 51     while(scanf("%d%d",&n,&m)!=EOF)
 52     {
 53         memset(last,-1,sizeof(last));
 54         memset(nex,-1,sizeof(nex));
 55         memset(pre,0,sizeof(pre));
 56         memset(vis,0,sizeof(vis));
 57         memset(a,0,sizeof(a));
 58         memset(c,0,sizeof(c));
 59         memset(answer,0,sizeof(answer));
 60         for(int i = 1; i <= n; i++)
 61         {
 62             scanf("%d",&a[i]);
 63             a[i+n] = a[i];
 64         }
 65         n*=2;
 66         pre[0] = 0;
 67         for(int i = 1; i <= n; i++)
 68         {
 69             if(!vis[a[i]])
 70             {
 71                 vis[a[i]] = 1;
 72                 pre[i] = pre[i-1]+1;
 73             }
 74             else{
 75                 pre[i] = pre[i-1];
 76             }
 77             if(~last[a[i]])
 78             {
 79                 nex[last[a[i]]] = i;
 80             }
 81             last[a[i]] = i;
 82         }
 83         for(int i = 0; i < m; i++)
 84         {
 85             scanf("%d%d",&querys[i].l,&querys[i].r);
 86             querys[i].l+=(n/2);
 87             swap(querys[i].l,querys[i].r);
 88             querys[i].id = i;
 89         }
 90         sort(querys,querys+m,cmp);
 91         int now = 1;
 92         for(int i = 0; i < m; i++)
 93         {
 94             while(now < querys[i].l)
 95             {
 96                 if(~nex[now])//如果当前位置的下一个位置存在,就更新树状数组
 97                     update(nex[now],1);//更新当前i位置的数a[i]的下一个下一个位置
 98                 now++;
 99             }
100             answer[querys[i].id] = pre[querys[i].r]-pre[querys[i].l-1]+query(querys[i].l,querys[i].r);
101         }
102         for(int i = 0; i < m; i++)
103             printf("%d\n",answer[i]);
104     }
105     return 0;
106 }

 

转载于:https://www.cnblogs.com/--lr/p/9364917.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值