题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
一行一个正整数 n,表示项链长度。
第二行 n 个正整数 a_i ,表示项链中第 i 个贝壳的种类。
第三行一个整数 m,表示 H 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。
输出格式
输出 m 行,每行一个整数,依次表示询问对应的答案。
思路: 对于 [ 1 , r ] [1,r] [1,r] 内不同颜色的球的个数,我们有一种思路去解决,我们开一个数组vis,比如在第x个位置出现了3这个颜色,那么我们就只把x位置置为1,之前3出现的位置都归为0,那么在vis中求 [ 1 , r ] [1,r] [1,r] 的前缀和 s u m ( 1 , r ) sum(1,r) sum(1,r) 即是此区间的颜色种类数。
然后我们考虑能不能利用上这个前缀和,由于我们都只保留的每种颜色的最后一个位置,那么在此区间内只有满足需要求的区间的右端点为 r 的前缀和的差值才是正确的,比如 s u m ( 1 , r ) − s u m ( 1 , l − 1 ) sum(1,r)-sum(1,l-1) sum(1,r)−sum(1,l−1) 是 [ l , r ] [l,r] [l,r] 区间内不同颜色的个数,但是 s u m ( 1 , j ) − s u m ( 1 , i − 1 ) , i ≤ j < r sum(1,j)-sum(1,i-1),i \le j< r sum(1,j)−sum(1,i−1),i≤j<r 就不是 [ i , j ] [i,j] [i,j] 的不同颜色个数。
所以我们就直接离线处理询问,把每次询问的区间按右端点从小到大排序,然后在遍历按上述处理即可。
由于要单点修改并求前缀和,所以就是树状数组比较方便了。
#include
using namespace std;
const int N = 1e6+7;
struct Node{
int l,r;
int i;
}q[N];
int tr[N];
int n,m;
int a[N],vis[N];
int ans[N];
int lowbit(int x){
return x&-x;
}
void add(int x,int v){
for(int i = x;i<=n;i+=lowbit(i))
tr[i] += v;
}
int query(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i)) ans+=tr[i];
return ans;
}
bool cmp(Node a,Node b){
return a.r<b.r;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].i = i;
}
sort(q+1,q+m+1,cmp);
int j = 1;
for(int i=1;i<=m;i++){
while(j <= q[i].r){
if(vis[a[j]]) add(vis[a[j]],-1);
add(j,1);
vis[a[j]] = j;
j++;
}
ans[q[i].i] = query(q[i].r) - query(q[i].l-1);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}