题意:给n个数,q个查询,每次询问序列中以第L个数为开头的后缀中,第x个数在这个后缀中是第几个出现的。
离线查询。
将查询按L从小到大排序,预处理每个数上一次出现的位置。
每次插入上一次出现位置在L之前的数,显然会将所有L之前的数都加入,然后查询这个数在后缀中第一次出现的位置(设为p)之前的前缀和。
由于L之前所有数都被加入,且[L,p]之间的所有数有且仅有被插入一次,所以就能得到了这个询问的答案。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=200005;
int a[MAXN], ans[MAXN], disc[MAXN];
int last[MAXN], bit[MAXN];
vector<int> pos[MAXN];
pair<int, int> pre[MAXN];
struct query{
int pos, p, idx, num;
bool operator < (const query& Rhs) const{
return pos < Rhs.pos;
}
}q[MAXN];
void add(int x, int v){
while(x<MAXN){
bit[x]+=v;
x+=x&(-x);
}
}
int query(int x){
int ret=0;
while(x>0){
ret+=bit[x];
x-=x&(-x);
}
return ret;
}
int main(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
int n; cin>>n;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
disc[i]=a[i];
}
sort(disc+1,disc+n+1);
int tot = unique(disc+1,disc+n+1)-disc;
for(int i=1; i<=n; i++){
a[i]=lower_bound(disc+1,disc+tot,a[i])-disc;
pos[a[i]].push_back(i);
}
int m; cin>>m;
for(int i=1; i<=m; i++){
scanf("%d%d",&q[i].pos,&q[i].p);
int x=a[q[i].pos+q[i].p-1];
q[i].idx=i; q[i].num=*lower_bound(pos[x].begin(),pos[x].end(),q[i].pos);
}
sort(q+1,q+m+1);
memset(last,-1,sizeof last);
for(int i=1; i<=n; i++){
pre[i]=make_pair(last[a[i]],i);
last[a[i]]=i;
}
sort(pre+1,pre+n+1);
for(int i=1, j=1; i<=m; i++){
for(; j<=n&&pre[j].first<q[i].pos; j++){
add(pre[j].second,1);
}
ans[q[i].idx]=query(q[i].num)-q[i].pos+1;
}
for(int i=1; i<=m; i++){
printf("%d\n",ans[i]);
}
return 0;
}