题目描述
传送门
题目大意:给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是
否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x
题解
刚开始想用线段树+bitset,但是发现会MLE。
所以就直接上莫队了。
对于
a∗b=x
,我们可以对x进行
O(n√)
因数分解,判断一下。
对于
a−b=x
,bitset中维护数a是否出现过,然后判断右移x后与原bitset按位and,如果有是1的位置,则可行
a+b=x
,一个bitset维护a,一个维护mx-a,然后令mx-x的那个右移(mx-x)与维护a的取and,如果有1的位置,则可行。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<bitset>
#define N 100003
using namespace std;
bitset<N> p,T,d;
int belong[N],a[N],cnt[N],cnt1[N],n,m,mx,ans[N];
struct data{
int id,x,l,r,opt;
}q[N];
int cmp(data a,data b){
return belong[a.l]<belong[b.l]||belong[a.l]==belong[b.l]&&a.r<b.r;
}
void change(int x,int v)
{
cnt[x]+=v;
if (cnt[x]==0&&v==-1) p[x]=0,d[mx-x]=0;
if (cnt[x]==1&&v==1) p[x]=1,d[mx-x]=1;
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),mx=max(mx,a[i]);
for (int i=1;i<=n;i++) scanf("%d%d%d%d",&q[i].opt,&q[i].l,&q[i].r,&q[i].x),q[i].id=i,mx=max(mx,q[i].x);
int blocksize=sqrt(n);
for (int i=1;i<=m;i++) belong[i]=(i-1)/blocksize+1;
int l=1;int r=1;
change(a[1],1);
sort(q+1,q+m+1,cmp);
for (int i=1;i<=m;i++) {
//cout<<q[i].l<<" "<<q[i].r<<" "<<q[i].x<<endl;
while (l<q[i].l) change(a[l++],-1);
while (l>q[i].l) change(a[--l],1);
while (r>q[i].r) change(a[r--],-1);
while (r<q[i].r) change(a[++r],1);
int t=q[i].id;
if (q[i].opt==3) {
for (int j=1;j*j<=q[i].x;j++)
if (q[i].x%j==0)
if (cnt[j]>0&&cnt[q[i].x/j]>0) {
ans[t]=1;
break;
}
}
if (q[i].opt==1) {
T=p;
T>>=q[i].x;
T=T&p;
if (T.count()) ans[t]=1;
}
if (q[i].opt==2) {
T=d;
T>>=(mx-q[i].x);
T=T&p;
if (T.count()) ans[t]=1;
}
}
for (int i=1;i<=m;i++)
if (ans[i]) printf("yuno\n");
else printf("yumi\n");
}