题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
一行一个正整数 n,表示项链长度。
第二行 n 个正整数 ai,表示项链中第 i 个贝壳的种类。
第三行一个整数 m,表示 HH 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。
输出格式
输出 m 行,每行一个整数,依次表示询问对应的答案。
输入输出样例
输入 #1复制
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出 #1复制
2
2
4
解析:
我们在记录离线做法上(P1972 [SDOI2009] HH的项链(线段树+离线做法+排序)-CSDN博客),进行改进创建n棵线段树即可。
用 一个 rt记录其删掉上次重复的 个数,在赋值到 当前 第 i棵 线段树上。
代码如下 :
// 线段树+离线处理查询
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1000005;
#define mid ((l+r)>>1)
int n,m,a[N],last[N];
int root[N],tot;
int ls[N*40],rs[N*40],sum[N*40];
void change(int &u,int v,int l,int r,int p,int k)
{
u = ++tot;
ls[u] = ls[v];
rs[u] = rs[v];
sum[u] = sum[v]+k;
if(l == r) return;
if(p <= mid)
change(ls[u],ls[v],l,mid,p,k);
else{
change(rs[u],rs[v],mid+1,r,p,k);
}
}
int query(int u,int l,int r,int p)
{
if(l==r) return sum[u];
if(p <= mid){
return query(ls[u],l,mid,p) + sum[rs[u]];
}
else return query(rs[u],mid+1,r,p);
}
int main()
{
scanf("%d",&n);
for(int i = 1;i<= n;i++){
scanf("%d",a+i);
}
for(int i = 1,rt;i <= n;i++)
{
if(!last[a[i]]) change(root[i],root[i-1],1,n,i,1); //没有出现过 直接 第 i个点 设置为1
else {
change(rt,root[i-1],1,n,last[a[i]],-1);
change(root[i],rt,1,n,i,1);
}
last[a[i]] = i; //记录了a[i]最后一个出现的位置
}
scanf("%d",&m);
int l,r;
while(m--)
{
scanf("%d%d",&l,&r);
printf("%d\n",query(root[r],1,n,l));//第r个版本的 左端点为 l的总和
}
return 0;
}
时间复杂度为:O(n*logn)