P1927 HH的项链 解题报告

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值