传送门

设现在莫队指针l,rl,r维护的区间中不同的数组成的集合为a1,a2,a3...an{a_1,a_2,a_3...a_n}

我们维护这样一个bitset\rm bitsetss,对于aia_is[ai]=1s[a_i]=1

Query1

考虑如何实现查询xy=nx-y=n,发现x=y+nx=y+n,所以对于所有出现在aa集合中的数aia_i,查询ai+na_i+naa集合中有没有出现即可。

这个可以通过操作(s \text{&} (s<<n)).any()实现,其中any()any()查询bitset\rm bitset中有没有11

Query2

考虑实现查询x+y=nx+y=n,其实本质和上面一种一样,就是化成x=y+nx=-y+n,再维护一个下标为ai-a_ibitset\rm bitsets1s1即可,但是这样做会有一个严重的问题,bitset\rm bitset下标不能为负数。

考虑给ai-a_i加上一个很大的正数NN,这里使用MAXN1MAXN-1

s1s1维护的数变为集合bi=ai+Nb_i=-a_i+N,原来的式子化成x=y+N+nNx=-y+N+n-N

查询的操作转换成查询bi+nNb_i+n-N有没有在aia_i中出现。

注意到nNn-N为负数,于是左移nNn-N转换成右移NnN-n

通过操作(s \text{&} (s1>>(N-n))).any()实现。

Query3

这个比较简单,将xy=nxy=n转换成x=nyx=\frac{n}{y},于是O(n)O(\sqrt{n})枚举nn的所有因数即可。

// luogu-judger-enable-o2
//小清新莫队题
#include <bits/stdc++.h>
#define MAXN 100005
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=(x<<3)+(x<<1)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
bitset<MAXN>s,s1;
int a[MAXN],cnt[MAXN],pos[MAXN];
// #define Add(x) if (cnt[x]++==0) s[x]=1,s1[MAXN-1-x]=1
// #define Del(x) if (--cnt[x]==0) s[x]=0,s1[MAXN-1-x]=0
inline void Add(int x){
    if (cnt[x]++==0) s[x]=1,s1[MAXN-1-x]=1;
}
inline void Del(int x){
    if (--cnt[x]==0) s[x]=0,s1[MAXN-1-x]=0;
}
struct Query{
    int opt,l,r,x,id;
}q[MAXN];
inline bool operator < (const Query &a,const Query &b){
	return (pos[a.l]^pos[b.l])?pos[a.l]<pos[b.l]:((pos[a.l]&1)?a.r<b.r:a.r>b.r);
}
int ans[MAXN];
int main(){
    int n=read(),m=read();
    int Size=(int)(sqrt(n));
    for (register int i=1;i<=n;++i){
        a[i]=read();
        pos[i]=(i-1)/Size+1;
    }
    for (register int i=1;i<=m;++i){
        int opt=read(),l=read(),r=read(),x=read();
        q[i]=Query{opt,l,r,x,i};
    }
    sort(q+1,q+1+m);
    int l=1,r=0;
    for (register 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]);
        if (q[i].opt==1){
            ans[q[i].id]=(s&(s<<(q[i].x))).any();
        }
        else if (q[i].opt==2){
            ans[q[i].id]=(s&(s1>>(MAXN-1-q[i].x))).any();
        }
        else if (q[i].opt==3){
            for (register int j=1;j*j<=q[i].x;++j){
                if (q[i].x%j!=0) continue;
                if (s[j]&&s[q[i].x/j]){
                    ans[q[i].id]=1;
                    break;
                }
            }
        }
    }
    for (register int i=1;i<=m;++i){
        puts(ans[i]==1?"hana":"bi");
    }
}

最后提一点需要特别注意,不能乱用define\rm define,比如说

#define Add(x) if (cnt[a[x]]) s[a[x]]=true;

调用时

Add(l++)

其实相当于

if (cnt[a[l++]]) s[a[l++]]=true

会导致WA\rm WA