HDU6964 I love counting (字典树+莫队)

题意:
给定一个长度为 n n n 的序列 c , q c,q c,q 次询问,每次给出 l , r , a , b l,r,a,b l,r,a,b求在 [ l , r ] [l,r] [l,r]中有多少种不同的值 k k k 满足 k ⊕ a ≤ b ​ . k⊕a≤b​. k⊕a≤b​.

题解:
莫队+字典树
补题的时候看到了带佬新得一种写法,即把字典树当成一颗完全二叉树去跑,不需要新建结点编号。

在 Trie 上顺着 a⊕b 走 ,为什么要跟着a⊕b走呢?
因为顺着a⊕b走也就是,顺着a⊕字典树=b走 当b=1时,就可以把c⊕a=0加入答案
若 b 当前位是 1,则所有 k⊕a 在当前位是 0 的数字都符合要求
找出字典树中与a当前为相同得值 (让其⊕后为0)

当然树状数组上套一个字典树也是一个不错得选择,先对r进行排序,然后离线询问,老套路题了。

代码:

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include<bits/stdc++.h>

using namespace std;
const int maxn=1e5+10;


struct Q{
    int l,r,k,a,b;
}pp[maxn];
int n,m,k;
int pos[maxn],a[maxn],cnt[maxn];
int sum[maxn*18],ans[maxn];
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return x*f;
}

struct rule{
    bool operator()(const Q & a,const Q & b)const{
    if(pos[a.l]!=pos[b.l]) return a.l<b.l;
    if(pos[a.l]&1) return a.r<b.r;
    return a.r>b.r;
    }
};

void add(int x,int v){
    int node=1;
    sum[node]+=v;
    for(int i=17;i>=0;i--){
        node=(node<<1)+(x>>i&1);
        sum[node]+=v;
    }
}

int query(int a,int b){
    int node=1,res=0;
    //cout<<"debug"<<endl;
    for(int i=17;i>=0;i--){  
    //在 Trie 上贴着 a^b 走  也就是说贴着a^字典树=b走 当b=1时,就可以把k^a=0加入答案
        int t=(a>>i&1)^(b>>i&1);
    //若 b 当前位是 1,则所有 k^a 在当前位是 0 的数字都符合要求
        if(b>>i&1) res+=sum[(node<<1)+(t^1)];
   //找出字典树中与a当前为相同得值 (让其^后为0)
        node=(node<<1)+t;
    }
    res+=sum[node];
    return res;
}


int main() {
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);

    n=read();
    int sz=sqrt(n);
    for(int i=1;i<=n;i++){
        a[i]=read();
        pos[i]=i/sz;
    }
    int q;
    q=read();
    for(int i=1;i<=q;i++){
        pp[i].l=read();
        pp[i].r=read();
        pp[i].a=read();
        pp[i].b=read();
        pp[i].k=i;
    }
    sort(pp+1,pp+1+q,rule());
    int l=1,r=0;
    for(int i=1;i<=q;i++){
        while(pp[i].l<l){
            l--;
            if(++cnt[a[l]]==1){
                add(a[l],1);
            }
        }
        while(pp[i].r>r){
            r++;
            if(++cnt[a[r]]==1){
                add(a[r],1);
            }
        }
        while(pp[i].l>l){
            if(--cnt[a[l]]==0){
                add(a[l],-1);
            }
            l++;
        }
        while(pp[i].r<r){
            if(--cnt[a[r]]==0){
                add(a[r],-1);
            }
            r--;
        }
        ans[pp[i].k]=query(pp[i].a,pp[i].b);
    }
    for(int i=1;i<=q;i++){
        printf("%d\n",ans[i]);
    }
}

上一篇:J Counting Triangles(牛客想训练赛3-J题)


下一篇:PAT——Counting Ones