CF877F题解

题目大意

有一个序列,每个位置上有 \(1\)\(2\) 两种元素若干,每次询问一个区间,求这个区间有多少个子区间满足 \(1\) 类元素恰好比 \(2\) 类元素多 \(k\) 个。

莫队

要是看到很难维护的东西,又允许离线,多半就是莫队。(雾)

来看看一个区间在满足条件的情况有啥可以用到的东西:

\[s1[R] - s1[L-1] = s2[R] - S2[L-1] + k \]

\(s1[i]\) 表示从 \(1\)\(i\) 有多少个一类元素,\(s2\) 同理。

移项:

\[(s1[R]-s2[R])=(s1[L-1]-s2[L-1])+k \]

转化成了这样一个问题:

\[\sum_{i=L-1}^{R-1}\sum_{j=i+1}^R[val[j]=val[i]+k] \]

其中 \(val[i]\) 表示 \(s1[i] - s2[i]\)

首先我们在莫队的过程中维护一个桶,表示目前区间中有多少个 \(val[i]\)

在莫队的转移过程中,我们得考虑新加入的元素是在左侧还是在右侧。

如果在左侧,\(ans+=Bucket[val[i]+k]\)(因为区间的右侧要比自己大);如果在右侧,则 \(ans+=Bucket[val[i]-k]\)

关于如何维护桶,离散化就行了。

记得离散化的数组不仅要加入 \(val[i],val[i]+k\)\(val[i]-k\),还要加入 \(0,k\)\(-k\)。(因为 \(val[0]=0\))

code:

#include<algorithm>
#include<cstdio>
#include<cmath>
const int M=1e5+5;
typedef long long ll;
int len;ll lsh[M*3];
int n,m,k,p,t[M],v[M],nxt[M],pre[M];ll CB[M*3];ll now,val[M],ans[M];
struct Query{
	int L,R,p,id;
	inline bool operator<(const Query&it)const{
		return p==it.p?p&1?R<it.R:R>it.R:L<it.L;
	}
}q[M];
inline void Add(const int&id,const int&to){
	now+=CB[to];++CB[v[id]];
}
inline void Del(const int&id,const int&to){
	--CB[v[id]];now-=CB[to];
}
signed main(){
	register int i,L=1,R=0;
	scanf("%d%d",&n,&k);lsh[++len]=0;lsh[++len]=k;lsh[++len]=-k;
	for(i=1;i<=n;++i)scanf("%d",t+i);
	for(i=1;i<=n;++i){
		scanf("%lld",val+i);
		if(t[i]==1)val[i]=val[i-1]+val[i];
		else val[i]=val[i-1]-val[i];
		lsh[++len]=val[i];lsh[++len]=val[i]-k;lsh[++len]=val[i]+k;
	}
	std::sort(lsh+1,lsh+len+1);len=std::unique(lsh+1,lsh+len+1)-lsh-1;
	for(i=0;i<=n;++i){
		v[i]=std::lower_bound(lsh+1,lsh+len+1,val[i])-lsh;
		pre[i]=std::lower_bound(lsh+1,lsh+len+1,val[i]-k)-lsh;
		nxt[i]=std::lower_bound(lsh+1,lsh+len+1,val[i]+k)-lsh;
	}
	scanf("%d",&m);p=ceil(n/sqrt(m*2.0/3));
	for(i=1;i<=m;++i){
		scanf("%d%d",&q[i].L,&q[i].R);--q[i].L;
		q[i].id=i;q[i].p=q[i].L/p;
	}
	std::sort(q+1,q+m+1);
	for(i=1;i<=m;++i){
		const int&QL=q[i].L,&QR=q[i].R;
		while(R<QR)++R,Add(R,pre[R]);
		while(L>QL)--L,Add(L,nxt[L]);
		while(L<QL)Del(L,nxt[L]),++L;
		while(R>QR)Del(R,pre[R]),--R;
		ans[q[i].id]=now;
	}
	for(i=1;i<=m;++i)printf("%lld\n",ans[i]);
}
posted @ 2022-01-10 16:04  Prean  阅读(30)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};