题目链接:https://www.luogu.org/problem/P3674
题目大意:给出一个序列,然后给出一些区间,询问在这些区间中是否存在两个数可以相加等于x,相减等于x,相乘等于x,如果有,那么我们就输出"hana",否则就输出"bi"。
解法:这里我们很容易的会想到莫队算法来记录其中的数字出现的次数,然后遍历一遍去寻找可能满足的数字,这里可能会T,因为莫队算法是采用分块思维的暴力算法,时间复杂度为O(N*len),这里的len是分块的长度,所以如果在每个m中再加一个O(n)的复杂度,那基本上就炸了,所以这里我们可以采用bitset优化一下,bitset是一种二进制的容器,在这里,它的每一位都可以看做是一个数的状态(存在或不存在)。
对于z+y=x存在的话,那么我们可以看做存在z=x-y,对于z-y=x存在的话,我们可以看做存在z=x+y,减法的时候我们很好寻找到x+y是否存在,那么我们要怎样去寻找x-y的状态呢?
我们先设y1=N-y,那么z=x-y=>z=x-(N-y1)=>z=y1+x-N,那么我们用一个bitset来存y1的状态,然后我们在加上x-n不就行了吗?
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
void read(int &x)
{
char c;
int f=1;
x=0;
c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9'){
x=x*10+c-'0';
c=getchar();
}
//return x*f;
}
bitset<maxn> now1,now2;
struct node
{
int l,r,pos,id,k,x;
bool operator <(node a)const{
return pos==a.pos?r<a.r:pos<a.pos;
}
}q[maxn];
int c[maxn],a[maxn],ans[maxn];
void add(int x){if(++c[x]==1)now1[x]=1,now2[maxn-7-x]=1;}
void del(int x){if(--c[x]==0)now1[x]=0,now2[maxn-7-x]=0;}
int main()
{
int n,m,len;
read(n),read(m);
len=sqrt(n);
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int i=1;i<=m;i++){
int l,r,k,x;
read(k),read(l),read(r),read(x);
q[i]=node{l,r,(l-1)/len+1,i,k,x};
}
sort(q+1,q+1+m);
int l=0,r=0;
for(int i=1;i<=m;i++){
while(l<q[i].l){
del(a[l++]);
}
while(l>q[i].l){
add(a[--l]);
}
while(r>q[i].r){
del(a[r--]);
}
while(r<q[i].r){
add(a[++r]);
}
int k=q[i].k,x=q[i].x;
switch(k){
case 1:
ans[q[i].id]=(now1&(now1<<x)).any();
break;
case 2:
ans[q[i].id]=(now1&(now2>>(maxn-x-7))).any();
break;
case 3:
for(int j=1;j*j<=x;++j){
if(x%j)continue;
if(now1[j]&&now1[x/j]){
ans[q[i].id]=1;break;
}
}
break;
}
}
for(int i=1;i<=m;i++){
ans[i]?printf("hana\n"):printf("bi\n");
}
return 0;
}