P1927 HH的项链 解题报告
题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
简而言之,求一段区间内不相同的数的个数
思路
这个题其实我在搞CF的时候见过类似的,那时候是要维护一个字母序列,所以可以直接写一个线段树莽过去
但是对于这个题肯定是不行的,因为这个数的范围太大了,所以会爆空间
由于我最近刚刚学习了莫队,然后就想用莫队来做,就是在指针移动的时候维护一个cnt数组,记录当前的数有几个,然后就愉悦的提交了一份代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFZ5ERK8-1684168050720)(C:\Users\宗子琪\Desktop\杂图\P1972 1.png)]
然后他就挂了
啥???
难道是我的常数不够优秀???
各种优化全开!!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5QHpbSQQ-1684168050729)(C:\Users\宗子琪\Desktop\杂图\QQ图片20191011103209.png)]
emmmmm
awsl
后来经过查验题解,我发现了一个问题:这个题卡莫队!!!
好了本题解结束
那这个题要怎么处理呢??
很明显的是,这个题是要用一个数据结构来优化,但是不幸的是,这个题所要维护的东西既不满足区间可加性,又不满足区间可减性,所以线段树和树状数组是不可以用的,至少在线做法是无法支持的。
仔细观察题面后可以发现,对于一个数x来说,它对一个询问区间 [ l , r ] [l,r] [l,r]来说,只有是在 r r r之前的最后一个x才会对这个答案产生贡献,然后我们就可以开一个数组,来记录当前这个数在哪个位置出现过,然后我们就可以通过对询问离线处理后,对右端点为第一关键字排个序,接下来就是一个简单的求前缀和的过程了,这个过程可以通过一个树状数组来维护
然后就过了
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 1100000
using namespace std;
int num[maxn],t[maxn],ans[maxn],vis[maxn];
int n,m;
inline int read(){
char c=getchar(); int x=0,f=1;
while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
}
struct Query{
int l,r,id;
bool operator < (const Query &a)const{
return r<a.r;
}
}q[maxn];
inline int lowbit(int x){
return x&(-x);
}
void add(int x,int k){
while(x<=n){
t[x]+=k;
x+=lowbit(x);
}
}
int sum(int x){
int ans=0;
while(x!=0){
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
num[i]=read();
}
cin>>m;
for(int i=1;i<=m;i++){
q[i].l=read();
q[i].r=read();
q[i].id=i;
}
sort(q+1,q+1+m);
int next=1;
for(int i=1;i<=m;i++){
for(int j=next;j<=q[i].r;j++){
if(vis[num[j]]){
add(vis[num[j]],-1);
}
add(j,1);
vis[num[j]]=j;
}
next=q[i].r+1;
ans[q[i].id]=sum(q[i].r)-sum(q[i].l-1);
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
return 0;
}