P4867 Gty的二逼妹子序列

链接:

P4867


题意:

给出长度为 \(n(1\leq n\leq 10^5)\) 的序列 \(s\),保证\(1\leq s_i\leq n\)。有 \(m(1\leq m\leq 10^6)\) 次询问,每次询问给出 \(4\) 个整数 \(l,r,a,b(1\leq l\leq r\leq n,1\leq a\leq b\leq n)\),询问在 \([l,r]\) 的序列中有多少权值属于 \([a,b]\)


分析:

感觉这道题和数颜色有一点像,区别是有值域的范围限制。看操作可以离线,所以首先就考虑莫队了,所以我开了两个树状数组存数量和种类,写出了大常数的 \(O(m\sqrt n\log n)\),开了 O2 还能卡过,不开就爆掉了

莫队移动的时间复杂度是 \(O((n+m)\sqrt n)\) 这里视为 \(O(m\sqrt n)\),已经很大了,所以对修改和查询的时间复杂度有比较大的要求。这里注意到 \(a,b\) 有着良好的性质:\(1\leq a\leq b\leq n\) 使得我们可以对值域进行分块,从而做到 \(O(1)\) 修改,\(O(\sqrt n)\) 查询,于是总的时间复杂度为 \(O(m\sqrt n)\)


代码:
#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=1e5+5;
const int Q=1e6+5;
int a[N],n,m,T,tl,tr;
struct ques{
	int l,r,L,R,o;
}q[Q];
inline int k(int x){return (x-1)/T+1;}
inline bool cmp(ques x,ques y){
	tl=k(x.l),tr=k(y.l);
	if(tl!=tr)return tl<tr;
	return (tl&1)?x.r>y.r:x.r<y.r;
}
int s[N],S[N];
void add(int x){s[x]++;S[k(x)]+=(s[x]==1);}
void del(int x){s[x]--;S[k(x)]-=(s[x]==0);}
int que(int l,int r){
	int res=0;tl=k(l),tr=k(r);
	if(tl==tr||tr-tl==1)
		for(int i=l;i<=r;i++) res+=s[i]?1:0;
	else{
		for(int i=tl+1;i<=tr-1;i++) res+=S[i];
		for(int i=l;i<=tl*T;i++) res+=s[i]?1:0;
		for(int i=(tr-1)*T+1;i<=r;i++) res+=s[i]?1:0;
	}
	return res;
}
int ans[Q];
signed main(){
	n=in,m=in;T=sqrt(n);
	for(int i=1;i<=n;i++)a[i]=in;
	for(int i=1;i<=m;i++)
		q[i].l=in,q[i].r=in,q[i].L=in,q[i].R=in,q[i].o=i;
	sort(q+1,q+1+m,cmp);
	int tl=1,tr=0;
	for(int i=1;i<=m;i++){
		int l=q[i].l,r=q[i].r,L=q[i].L,R=q[i].R,o=q[i].o;
		while(tr<r)add(a[++tr]);
		while(tr>r)del(a[tr--]);
		while(tl<l)del(a[tl++]);
		while(tl>l)add(a[--tl]);
		ans[o]=que(L,R);
	}
	for(int i=1;i<=m;i++)
		cout<<ans[i]<<'\n';
	return 0;
}

posted @ 2021-08-29 15:46  llmmkk  阅读(65)  评论(0编辑  收藏  举报