思路:

如果不考虑去重,一般都会想到可持久化字典树。

的个数满足,很明显用字典树可以轻松的做到。求区间的内的个数的话加个可持久化,前个数组成第个版本的字典树,然后第个版本算出的值减去第个版本算出的值就是答案。

字典树求的个数满足的原理:

构建字典树时,每个节点存这个节点包含了多少个数。

搜索字典树时,假设当前访问到的第个二进制位,的这一位为。如果这一位为,字典树下一个节点的值如果是,那么得到的数一定是满足条件的,直接返回这个节点包含了多少数,然后继续访问值为的节点;如果这一位为,继续访问值为的节点。如果搜索到了字典树的叶子节点,那么直接返回这个节点包含了多少数。

考虑到一个节点内会包含多个相同的值,为了避免重复计数,不能直接记录这个节点包含了多少个数。处理区间内不同种类的数,套路一般是记录每个数的后缀,于是可以在构建字典树时,每个节点存经过这个节点的数的下标以及后缀。

搜索字典树时,假设当前节点中的数都满足,那么把询问插入这个节点中,该询问的后缀为其右端点,同时标记这条信息为询问。

遍历每个节点,按后缀从大到小取出存入的信息,如果该信息不是询问,用树状数组维护这条信息的下标,如果是询问,用树状数组查询有多少个下标在区间中。处理完一个节点后立马把树状数组情空。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int pw[25],t[maxn*20][2],head[maxn],c[maxn],Next[maxn],ans[maxn],tot;
struct node{
    int l,r,x,id;
    bool operator<(const node& b)const{
        if(x!=b.x) return x>b.x;
        else if(id!=b.id) return id>b.id;
        else return l<b.l;
    }
};
vector<node>v[maxn*20];
int sum[maxn],n;
inline void add(int p,int v) {
    while(p<=n) {
        sum[p]+=v; p+=(p&-p);
    }
}
inline int query(int p) {
    int res(0);
    while(p) {
        res+=sum[p];    p-=(p&-p);
    }
    return res;
}
inline void ins(int x,int id) {
    int root(0);
    for(int i=18,p;~i;--i) {
        if(x&pw[i]) p=1;
        else p=0;
        if(!t[root][p]) t[root][p]=++tot;
        root=t[root][p];
        v[root].emplace_back(node{id,0,Next[id],0});
    }
}
void query(int l,int r,int x,int y,int id) {
    int root=0,v1,v2,p;
    for(int i=18;~i;--i) {
        v1=(x&pw[i])!=0,v2=(y&pw[i])!=0;
        if(v2) {
            if(t[root][v1]) v[t[root][v1]].emplace_back(node{l,r,r,id});
            root=t[root][v1^1];
            if(!root) return;
        }
        else {
            root=t[root][v1];
            if(!root) return;
        }
    }
    v[root].emplace_back(node{l,r,r,id});
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    pw[0]=1;
    for(int i=1;i<=18;++i) pw[i]=pw[i-1]*2;
    int Q,l,r,a,b;
    cin>>n;
    for(int i=1;i<=n;++i) cin>>c[i];
    for(int i=n;i;--i) {
        if(!head[c[i]]) Next[i]=n+1;
        else Next[i]=head[c[i]];
        head[c[i]]=i;
        ins(c[i],i);
    }
    cin>>Q;
    for(int i=1;i<=Q;++i) {
        cin>>l>>r>>a>>b;
        query(l,r,a,b,i);
    }
    for(int i=1;i<=tot;++i) {
        sort(v[i].begin(),v[i].end());

        for(node x:v[i]) {
            if(!x.id) add(x.l,1);
            else ans[x.id]+=query(x.r)-query(x.l-1);
        }
        for(node x:v[i]) if(!x.id) add(x.l,-1);
    }
    for(int i=1;i<=Q;++i) printf("%d\n",ans[i]);
    return 0;
}