【洛谷5355】[Ynoi2017] 由乃的玉米田(莫队+bitset)

点此看题面

  • 给定一个长度为(n)的序列。
  • (q)次询问,每次要求判断一段区间是否能选出两个数(可以在同一位置),使得这两个数为差(x)/和为(x)/积为(x)/商为(x)(无余数)。
  • (n,q,Vle10^5)

减、加、乘的简单莫队

只有加、减、乘操作的话是非常水的。

我们只要维护好两个(bitset),分别记录每个数(v)的存在性((s1))和每个数的相反数(MAX-v)的存在性((s2))。

对于减法,我们把(s1)左移(x)位(相当于给所有数加上了(x)),然后和(s1)按位与在一起,如果依然有(1),就说明这两个数差为(x)

对于加法,我们把(s1)左移(MAX-x)(s2)按位与在一起,如果有(1),就说明(v1+MAX-x=MAX-v2),移项便可见这两个数和为(x)

对于乘法,我们直接暴力枚举它的所有小于等于(O(sqrt n))的因子,然后只要到(bitset)中询问是否同时存在一对乘起来等于(x)的因子即可。由于这个复杂度和莫队是独立的,因此也是正确的。

除法的根号分治

首先考虑如果询问的(x)大于(sqrt {MAX}),我们可以把它放到前面的莫队里一起做,只要(O(sqrt {MAX}))暴力枚举倍数判断是否存在即可。

否则那么(xlesqrt{MAX}),那么我们只要枚举每种(x)分别暴力即可。

具体我们把询问离线按右端点排序,向右扫的过程中维护好每个数上次出现的位置(lst_v),然后考虑如果一个位置(i)能作为右端点产生贡献,当且仅当在(i)之前最后一个(a_i imes x)(a_idiv x)所在的位置(可以直接询问(lst))和(i)都在区间内。

所以我们枚举右端点,每次将当前位置计入可以考虑的范围,然后就只要维护最大的左边能产生贡献的位置(t),每次询问是判一下询问的左端点是否小于等于(t)即可。

代码:(O(nsqrt n))

#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
#define S 320
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,m,a[N+5],sz,bl[N+5],ans[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	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...);}
}using namespace FastIO;
namespace Mo//莫队处理减、加、乘、大数除
{
	int Qt;struct Q
	{
		int p,l,r,op,x;I Q(CI i=0,CI a=0,CI b=0,CI g=0,CI v=0):p(i),l(a),r(b),op(g),x(v){}
		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[N+5];
	int c[N+5];bitset<N+5> s1,s2;I void Solve()
	{
		RI i,j,L=1,R=0;for(sort(q+1,q+Qt+1),i=1;i<=Qt;++i)
		{
			#define A(x) (!c[x]++&&(s1.set(x),s2.set(N-x),0))
			#define D(x) (!--c[x]&&(s1.reset(x),s2.reset(N-x),0))
			W(R<q[i].r) ++R,A(a[R]);W(L>q[i].l) --L,A(a[L]);W(R>q[i].r) D(a[R]),--R;W(L<q[i].l) D(a[L]),++L;//移动区间
			if(q[i].op==1) {ans[q[i].p]=((s1<<q[i].x)&s1).any();continue;}//减法
			if(q[i].op==2) {ans[q[i].p]=((s1<<N-q[i].x)&s2).any();continue;}//加法
			if(q[i].op==3) {if(!q[i].x) ans[q[i].p]=s1.test(0);else for(j=1;j*j<=q[i].x;++j)//乘法
				if(!(q[i].x%j)&&s1.test(j)&&s1.test(q[i].x/j)) {ans[q[i].p]=1;break;}continue;}//枚举一对因数判断
			for(j=1;q[i].x*j<=N;++j) if(s1.test(j)&&s1.test(q[i].x*j)) {ans[q[i].p]=1;break;}//除法,暴枚倍数
		}
	}
}
namespace BF//暴力处理小数除
{
	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 r<o.r;};
	};vector<Q> q[S+5];
	int lst[N+5];I void Solve(CI x)
	{
		RI i,j=0,qs=q[x].size(),t=0;for(sort(q[x].begin(),q[x].end()),i=0;i<=N;++i) lst[i]=0;//按右端点排序,清空lst
		for(i=1;i<=n;lst[a[i]]=i,++i) {a[i]*x<=N&&Gmax(t,lst[a[i]*x]),//a[i]×x
			!(a[i]%x)&&Gmax(t,lst[a[i]/x]);W(j^qs&&q[x][j].r==i) ans[q[x][j].p]=q[x][j].l<=t,++j;}//a[i]÷x,询问只要将左端点与t比较
	}
}
int main()
{
	RI i,j;for(read(n),read(m),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),bl[i]=(i-1)/sz+1;
	RI op,l,r,x;for(i=1;i<=m;++i) read(op,l,r,x),op^4||x>S
		?(Mo::q[++Mo::Qt]=Mo::Q(i,l,r,op,x),0):(x^1?(BF::q[x].push_back(BF::Q(i,l,r)),0):ans[i]=1);//把询问分类
	for(Mo::Solve(),i=2;i<=S;++i) !BF::q[i].empty()&&(BF::Solve(i),0);//先统一莫队,再枚举小除数暴力
	for(i=1;i<=m;++i) puts(ans[i]?"yuno":"yumi");return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5355.html