Description
给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x ,这三个操作分别为操作1,2,3选出的这两个数可以是同一个位置的数
Solution
一开始直接想到的是莫队,但莫队归莫队,怎么处理这三个操作呢?看看了大神题解,又听了一番同市神犇的指点,终于学会了一点
bitset
的应用。
对
bitset
的学习,可以参考这篇:传送
用
bitset
维护每个值是否出现,再用一个
s
数组来保存这个值出现了几次,这样维护一下就好了。
乘法操作,直接通过
减法操作,相当于是查询是否存在ii,jj满足
a[i]−a[j]=x
的形式,那么把
bitset
右移x位与原来的&一下看新的二进制数是否为0(用
bitset
自带的
count
就好了)
加法类似,不过需要维护的是
a[i]+a[j]=x
的形式,那么我用
c
(
复杂度
O(n32+n232)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cmath>
#include<iostream>
using namespace std;
const int N = 100010;
bitset<N> f,g;
int a[N],ans[N],pos[N],s[N];
int n,m;
struct node{int id,o,l,r,x;}qu[N];
inline bool cmp(node a,node b) {
return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
}
int main()
{
scanf("%d%d",&n,&m);
int bs = (int)sqrt(n);
for(int i = 1;i <= n;i++) {
scanf("%d",&a[i]);
pos[i] = (i-1)/bs;
}
for(int i = 1;i <= m;i++) {
scanf("%d%d%d%d",&qu[i].o,&qu[i].l,&qu[i].r,&qu[i].x);
qu[i].id = i;
}
sort(qu+1,qu+m+1,cmp);
int pl = 1,pr = 0;
memset(s,0,sizeof(s));
for(int i = 1;i <= m;i++)
{
if(qu[i].l == qu[i].r){ans[qu[i].id] = 0;continue;}
if(pr < qu[i].r)
for(int j = pr+1;j <= qu[i].r;j++) {
s[a[j]]++; f[a[j]]=1; g[N-a[j]]=1;
}
else
for(int j = pr;j > qu[i].r;j--) {
s[a[j]]--;
if(!s[a[j]]){f[a[j]]=0;g[N-a[j]]=0;}
}
pr = qu[i].r;
if(pl < qu[i].l)
for(int j = pl;j < qu[i].l;j++) {
s[a[j]]--;
if(!s[a[j]]){f[a[j]]=0;g[N-a[j]]=0;}
}
else
for(int j = pl-1;j >= qu[i].l;j--) {
s[a[j]]++; f[a[j]]=1; g[N-a[j]]=1;
}
pl = qu[i].l;
if(qu[i].o == 1)
ans[qu[i].id] = ((f>>qu[i].x)&f).any();
if(qu[i].o == 2)
ans[qu[i].id] = ((g>>(N-qu[i].x))&f).any();
if(qu[i].o == 3) {
if(qu[i].x == 0 && f[0])
ans[qu[i].id] = 1;
for(int j = 1;j*j<=qu[i].x;j++)
if(!(qu[i].x%j))
if(f[j]&f[qu[i].x/j]) {
ans[qu[i].id] = 1;
break;
}
}
}
for(int i = 1;i <= m;i++)
printf(ans[i] ? "yuno\n" : "yumi\n");
return 0;
}