把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷4688】[Ynoi2016] 掉进兔子洞(莫队+bitset)

点此看题面

  • 给定一个长度为\(n\)的序列。
  • \(q\)次询问,每次给定三个区间,把三个区间中同时出现的数一个一个删掉,求最后剩下的数的总个数。
  • \(n,m\le10^5\)

经典\(Ynoi\)套餐:莫队+\(bitset\)

作为一道\(Ynoi\)来说应该算是比较水的了吧。

显然,答案应该是三个区间中数的总个数减去\(3\times\)在三个区间中同时出现的数的个数

假设一个区间中没有重复的数,我们只要用\(bitset\)就能维护出这个区间中出现了哪些数,把三个区间的\(bitset\)按位与在一起,剩余的\(1\)的个数就是在三个区间中同时出现的数的个数。

但问题在于有重复的数,不过其实没什么大关系。只要我们在离散化的时候不对排序后的数组使用\(unique\),然后假设一个数离散化后对应的数为\(x\),那么第二次出现就令它等于\(x+1\),第三次出现等于\(x+2\),那么就可以让重复的数变得不再重复了。

而要求出这样子的一个\(bitset\),显然只能考虑莫队了。

注意\(n\times m\)\(bitset\)开不下,一个经典处理方式把询问分成若干组分别搞(例如\(4\)组),然后就能卡过内存了。

代码:\(O(n\sqrt n+\frac{nm}{32})\)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m,a[N+5],p[N+5],dv[N+5],sz,bl[N+5],ans[N/4+5];bitset<N+5> s,S[N/4+5];
struct Q
{
	int p,l,r;I Q(CI i=0,CI a=0,CI b=0):p(i),l(a),r(b){}
	I bool operator < (Con Q& o) Con {return bl[l]^bl[o.l]?l<o.l:(bl[l]&1?r<o.r:r>o.r);}
}q[3*N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
I void Mo(Q* q,CI m)//莫队
{
	#define A(x) (s.set(p[x]++))//插入一个元素
	#define D(x) (s.reset(--p[x]))//删除一个元素
	RI i,L=1,R=0;for(sort(q+1,q+m+1),i=1;i<=m;ans[q[i].p]+=q[i].r-q[i].l+1,S[q[i].p]&=s,++i)//统计每个询问的结果
		{W(R<q[i].r) A(a[++R]);W(L>q[i].l) A(a[--L]);W(R>q[i].r) D(a[R--]);W(L<q[i].l) D(a[L++]);}//移动区间
	W(L<=R) D(a[L++]);//清空
}
I void Solve(CI l,CI r)//处理[l,r]中的询问
{
	RI i;for(i=l;i<=r;++i) ans[q[3*i-2].p=q[3*i-1].p=q[3*i].p=i-l+1]=0,S[i-l+1].set();
	for(Mo(q+3*(l-1),3*(r-l+1)),i=1;i<=r-l+1;++i) writeln(ans[i]-3*S[i].count());//总个数-3*共同个数
}
int main()
{
	RI i;for(read(n,m),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),dv[i]=a[i],bl[i]=(i-1)/sz+1;//对序列分块
	for(i=1;i<=m;++i) read(q[3*i-2].l,q[3*i-2].r,q[3*i-1].l,q[3*i-1].r,q[3*i].l,q[3*i].r);
	for(sort(dv+1,dv+n+1),i=1;i<=n;++i) p[a[i]=lower_bound(dv+1,dv+n+1,a[i])-dv]=a[i];//离散化,不unique
	m<=N/4?Solve(1,m):(Solve(1,m/4),Solve(m/4+1,m/4*2),Solve(m/4*2+1,m/4*3),Solve(m/4*3+1,m));//分4组求解卡内存
	return clear(),0;
}
posted @ 2021-03-25 18:53  TheLostWeak  阅读(28)  评论(0编辑  收藏  举报