题目传送门
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
一行一个正整数 n,表示项链长度。
第二行 n 个正整数 a_i,表示项链中第 i 个贝壳的种类。
第三行一个整数 m,表示 H 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。
输出 m行,每行一个整数,依次表示询问对应的答案。
输入 #1
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出 #1
2
2
4
其实你如果理解了题意 你会觉得这题很简单 (不是吗)
但它的数据:对于 100%的数据,1 ≤n,m,a_i ≤10^6,1 ≤l ≤r ≤n
这昭示我们:别想着暴力了 离散化也不行
所以我们可采取的做法有:
线段树 主席树 树状数组 离线 莫队 以及二分和前缀和
蒟蒻的我只能用线段树了
过程也就是针对每个步骤分别写函数了
当然如果能用离线维护区间就更好了
#include
#include
#include
#define p 1000001
#pragma GCC optimize(2)
using namespace std;
int a[p],qy[p],pos[p],ans[p];
struct node{
int l,r,v;
}vis[p<<6];
struct node2{
int l,r,id;
bool operator<(const node2&a)const{ //上网找的 就是个简化cmp
return r<a.r;
}
}q[p];
void build(int l,int r,int dep) //建树
{
if(l==r){
vis[dep].l=vis[dep].r=l;
return ;
}
vis[dep].l=l;
vis[dep].r=r;
int mid=(l+r)>>1;
build(l,mid,dep*2);
build(mid+1,r,dep*2+1);
}
void next(int dep,int pos,int k) //查找下一个点
{
if(vis[dep].r==vis[dep].l){
vis[dep].v=k;
return ;
}
int mid=(vis[dep].r+vis[dep].l)>>1;
if(pos<=mid){
next(dep*2,pos,k);
}else{
next(dep*2+1,pos,k);
}
vis[dep].v=vis[dep*2].v+vis[dep*2+1].v;
}
int ques(int dep,int l,int r) //询问 返回计数的值
{
if(vis[dep].l==l&&vis[dep].r==r){
return vis[dep].v;
}
int mid=(vis[dep].l+vis[dep].r)>>1;
if(r<=mid){
return ques(dep*2,l,r);
}else if(l>mid){
return ques(dep*2+1,l,r);
}
return ques(dep*2,l,mid)+ques(dep*2+1,mid+1,r);
}
int main()
{
build(1,p,1); //先建树
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a[i]);
qy[i]=pos[a[i]]; //记录坐标
pos[a[i]]=i;
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i; //记录区间坐标
}
sort(q+1,q+m+1); //最好用stable_sort(),因为没有cmp的结构体排序,sort()不稳定
//当然如果你直接写cmp就没事了
int nxtr=1;
for(int i=1;i<=m;i++){
int l=q[i].l;
int r=q[i].r;
for(int i=nxtr;i<=r;i++){
if(qy[i]!=0) //查找了
next(1,qy[i],0); //就搜下一个
next(1,i,1);
}
ans[q[i].id]=ques(1,l,r); //赋值询问的答案
nxtr=r+1;
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}