题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
一行一个正整数 n,表示项链长度。
第二行 n 个正整数 a_i,表示项链中第 i 个贝壳的种类。
第三行一个整数 m,表示 HH 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。
1 ≤ n , m , a i ≤ 1 0 6 , 1 ≤ l ≤ r ≤ n 1≤n,m,a_i ≤10 ^6 ,1\leq l \leq r \leq n 1≤n,m,ai≤106,1≤l≤r≤n。
输出格式
输出 m 行,每行一个整数,依次表示询问对应的答案。
题目分析
首先在树状数组中将每个种类贝克第一次出现的位置+1
令nxt[i]表示与贝壳 i 种类相同的下一个贝壳的位置
对某个询问[l,r],对所有
1
≤
k
<
l
1\leq k < l
1≤k<l将树状数组中
n
x
t
[
k
]
nxt[k]
nxt[k]位置+1
这样可以保证[l,r]内每个不同数字都只有一个位置在树状数组中被加了一,由此就可以在树状数组中查询前缀和回答不同数字的个数了
而初始时将询问按左端点升序排序,将树状数组中加入nxt[k]位置的操作就是线性的次数的
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-x))
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=2000010;
int n,m;
struct Query{int ll,rr,id;}q[maxn];
int a[maxn];
int vis[maxn],nxt[maxn],last[maxn];
int sum[maxn],ans[maxn];
bool cmp(Query a,Query b){ return a.ll<b.ll;}
void add(int x){
for(int i=x;i<=n;i+=lowbit(i)) sum[i]++;
}
int qsum(int x)
{
int res=0;
for(int i=x;i>0;i-=lowbit(i)) res+=sum[i];
return res;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(!vis[a[i]])
{
vis[a[i]]=1;
add(i);
}
}
for(int i=n;i>=1;--i)
{
if(last[a[i]]==0) nxt[i]=n+1;
else nxt[i]=last[a[i]];
last[a[i]]=i;
}
m=read();
for(int i=1;i<=m;++i)
{
q[i].ll=read();
q[i].rr=read();
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int idx=1;
for(int i=1;i<=m;++i)
{
while(idx<q[i].ll) add(nxt[idx++]);
ans[q[i].id]=qsum(q[i].rr)-qsum(q[i].ll-1);
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}