CF-817-E-Trie

817-E 题目大意

给定一个初始为空的可重集S。现有Q次操作,操作的类型有三种:
1,x:向集合S中加入一个x
2,x:从集合S中删除一个x,数据保证x存在。
3,x,l:询问集合S中有多少个数异或上x的结果小于l


Solution

要求一个集合可插入,可删除,且询问异或相关的结果,第一想法肯定是想到01Trie

操作一和操作二很容易实现,剩下的就是需要考虑操作三如何统计:
Trie中查询集合中元素和x的异或值,当前枚举到第i位,在Trie中走到了节点p,分两类情况:
1、如果l的第i位为1,则p所在子树中的元素第i位与x相同的元素与x异或的结果一定小于l,我们直接加上这一部分的元素数量,然后p走到与x的第i位不同的子树中去,寻找其他的可能满足条件的元素。
2、如果l的第i位为0,则p所在子树中的元素第i位与x不同的元素与x异或的结果一定不小于l,此时p应当走到与x的第i位相同的子树中去查找元素。

时间复杂度O(nlogn)

  • 要注意的是,如果p走到了0号点,说明不存在合法的元素了,直接return即可。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;

const int N=1e5+10;
int ch[N*31][2],cnt[N*31];
int idx=0;

void update(int x,int k){
    int p=0;
    for(int i=30;~i;i--){
        int j=(x>>i)&1;
        if(!ch[p][j]) ch[p][j]=++idx;
        p=ch[p][j];
        cnt[p]+=k;
    }
}

int query(int x,int l){
    int res=0;
    int p=0;
    for(int i=30;~i;i--){
        int j=(x>>i)&1;
        int k=(l>>i)&1;
        if(!k){
            p=ch[p][j];
        }else{
            res+=cnt[ch[p][j]];
            p=ch[p][!j];
        }
        if(!p) break;
    }
    return res;
}

void solve(){
    int q;
    cin>>q;
    while(q--){
        int op,x,l;
        cin>>op>>x;
        if(op==1){
            update(x,1);
        }else if(op==2){
            update(x,-1);
        }else{
            cin>>l;
            cout<<query(x,l)<<'\n';
        }
    }
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   fengxue-K  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示