题意:
给出个数,求
,
这两个区间不同数的个数
思路:
其实只要把区间扩大一倍,就是求这个区间了
定义数组中第一次出现的数的后缀为,第
次出现的数的后缀为该数第
次出先时的下标。比如:原数组为
,扩大一倍后为
,每个数对应的后缀为
。
如果某个数出现多次,而我们只算最右边的那个,我们会发现最右边的那个数的后缀一定是大于区间右端点的,其它的数的后缀一定是小于等于区间右端点的,所以我们只要把后缀大于区间右端点的数的下标取出来,然后用树状数组计算区间内有多少个数。
code;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
int head[maxn],c[maxn],Next[maxn],ans[maxn];
struct node {
int l,r,x,id;
bool operator<(const node& b)const {
if(x!=b.x) return x>b.x;
else if(id!=b.id) return id>b.id;
else return l<b.l;
}
};
vector<node>v;
int sum[maxn],m;
inline void add(int p,int v) {
while(p<=m) {
sum[p]+=v;
p+=(p&-p);
}
}
inline int query(int p) {
int res(0);
while(p) {
res+=sum[p];
p-=(p&-p);
}
return res;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n,Q;
while(cin>>n>>Q) {
for(int i=1; i<=n; ++i) cin>>c[i],c[i+n]=c[i];
m=n*2;
for(int i=m; i; --i) {
if(!head[c[i]]) Next[i]=m+1;
else Next[i]=head[c[i]];
head[c[i]]=i;
v.emplace_back(node {i,0,Next[i],0});
}
for(int i=1,l,r; i<=Q; ++i) {
cin>>l>>r;
v.emplace_back(node {r,n+l,n+l,i});
}
sort(v.begin(),v.end());
for(node i:v) {
if(!i.id) add(i.l,1);
else ans[i.id]+=query(i.r)-query(i.l-1);
}
for(int i=1; i<=Q; ++i) {
cout<<ans[i]<<'\n';
ans[i]=0;
}
for(node i:v) if(!i.id) add(i.l,-1);
for(int i=1; i<=n; ++i) head[c[i]]=0;
v.clear();
}
return 0;
}

京公网安备 11010502036488号