链接:https://www.nowcoder.com/acm/contest/139/J
来源:牛客网
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
Given a sequence of integers a1, a2, ..., an and q pairs of integers (l1, r1), (l2, r2), ..., (lq, rq), find count(l1, r1), count(l2, r2), ..., count(lq, rq) where count(i, j) is the number of different integers among a1, a2, ..., ai, aj, aj + 1, ..., 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 n and q.
The second line contains n integers a1, a2, ..., an.
The i-th of the following q lines contains two integers li and ri.
输出描述:
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 ≤ 105
* 1 ≤ ai ≤ n
* 1 ≤ li, ri ≤ n
* The number of test cases does not exceed 10.
题意:给出一串数字以及q次查询,每次查询给出[l,r],要求求出[1,l]+[r,n]的所有不相同的数字个数。
思路:第一种思路是先对数组进行倍增,变为两倍长,然后查询就变成一个完整的区间,离线处理,按r从小到大排序,数组从1到2n扫一遍,每次更新每种数组最后出现的位置,用树状数组处理,把前一次出现位置在树状数组里面更新-1,这次位置更新+1,然后如果扫描到的i==r则对查询区间进行sum查询。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 100005;
struct node
{
int l,r;
int id;
}stu[maxn];
bool cmp(node a,node b)
{
return a.r<b.r;
}
int n,q;
int c[maxn*2], a[maxn*2];
int last[maxn];
int ans[maxn];
int Lowbit(int x) // 2^k Lowbit(x)为x保留最右边1的结果
{
return x&(-x);
}
void update(int i, int x)//i点增量为x ,更新树状数组
{
while(i <= n*2)
{
c[i] += x;
i += Lowbit(i);
}
}
int sum(int x)//区间求和 [1,x]
{
int sum=0;
while(x>0)
{
sum+=c[x];
x-=Lowbit(x);
}
return sum;
}
int Getsum(int x1,int x2) //求任意区间和
{
return sum(x2) - sum(x1-1);
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
memset(last,0,sizeof(last));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
int x,y;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
stu[i].l=y;
stu[i].r=n+x;
stu[i].id=i;
}
sort(stu+1,stu+1+q,cmp);
int k=1;
for(int i=1;i<=2*n&&k<=q;i++)
{
if(last[a[i]])
update(last[a[i]],-1);
last[a[i]]=i;
update(last[a[i]],1);
while(k<=q&&stu[k].r<=i)
{
ans[stu[k].id]=Getsum(stu[k].l,stu[k].r);
k++;
}
}
for(int i=1;i<=q;i++)
{
printf("%d\n",ans[i]);
}
}
}
第二种思路:差不多,需要记录下每个数字第一次出现位置,及最后一次出现位置,不需要倍增,查询按r从小到大排序,数组1到n扫一遍,树状数组记录到目前的r为止,每个对应的l,中间段的与两边不同的数字个数,如果i==r,进行查询,总数减去查询的sum,如果这个i是a[i]最后出现的位置,那么找到对应的第一次出现位置first,first之前的l其中间段的数字数需要++。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 100005;
struct node
{
int l,r;
int id;
}stu[maxn];
bool cmp(node a,node b)
{
return a.r<b.r;
}
int n,q;
int c[maxn*2], a[maxn*2];
int last[maxn];
int ans[maxn];
int Lowbit(int x) // 2^k Lowbit(x)为x保留最右边1的结果
{
return x&(-x);
}
void update(int i, int x)//i点增量为x ,更新树状数组
{
while(i <= n*2)
{
c[i] += x;
i += Lowbit(i);
}
}
int sum(int x)//区间求和 [1,x]
{
int sum=0;
while(x>0)
{
sum+=c[x];
x-=Lowbit(x);
}
return sum;
}
int Getsum(int x1,int x2) //求任意区间和
{
return sum(x2) - sum(x1-1);
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
memset(last,0,sizeof(last));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
int x,y;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
stu[i].l=y;
stu[i].r=n+x;
stu[i].id=i;
}
sort(stu+1,stu+1+q,cmp);
int k=1;
for(int i=1;i<=2*n&&k<=q;i++)
{
if(last[a[i]])
update(last[a[i]],-1);
last[a[i]]=i;
update(last[a[i]],1);
while(k<=q&&stu[k].r<=i)
{
ans[stu[k].id]=Getsum(stu[k].l,stu[k].r);
k++;
}
}
for(int i=1;i<=q;i++)
{
printf("%d\n",ans[i]);
}
}
}