【cf842D】Vitya and Strange Lesson(01字典树)

D. Vitya and Strange Lesson

题意

数列里有n个数,m次操作,每次给x,让n个数都异或上x。并输出数列的mex值。

题解

01字典树保存每个节点下面有几个数,然后当前总异或的是sw,则sw为1的位的节点左右孩子交换(不用真的交换)。左孩子的值小于左边总节点数则mex在左子树,否则在右子树。

代码

const int N=531000;//3e5<2^19<N
int sw=0;
struct Trie{
    int ch[N*20][2];
    int cnt[N*20];
    int size;
    void Build(int node, int pos){
        if(pos<0)return;
        rep(i,0,2){
            ch[node][i]=++size;
            cnt[size]=0;
            Build(ch[node][i], pos-1);
        }
    }
    void Init(){
        size=0;
        Build(0,19);
    }
    void Insert(int node, int pos, int num){
        if(pos<0)cnt[node]=1;
        if(pos<0) return;
        Insert(ch[node][(num>>pos)&1], pos-1, num);
        cnt[node]=cnt[ch[node][0]]+cnt[ch[node][1]];
    }
    int Query(int node, int pos, int num){
        if(pos<0)
            return num;
        int lson=(sw&(1<<pos))?1:0;
        if(cnt[ch[node][lson]]<(1<<pos)){
            return Query(ch[node][lson], pos-1, num);
        }
        return Query(ch[node][!lson], pos-1,num+(1<<pos));
    }
}trie;
int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        trie.Init();
        sw=0;
        rep(i,0,n){
            int x;
            scanf("%d",&x);
            trie.Insert(0,19,x);
        }
        while(m--){
            int x;
            scanf("%d",&x);
            sw^=x;
            printf("%d\n",trie.Query(0,19,0));
        }
        puts("");
    }
    return 0;
}

自从用了cf上偷来的开头模板以后,感觉写代码速度也快了。但是,代码像裙子越短越性感。所以博客上就不放头文件了。
其实可以像线段树一样写,写得更短了哈哈:

const int N=531000;
int sw=0;
struct Trie{
    int cnt[N*20];
    void Insert(int node, int pos, int num){
        if(pos<0){cnt[node]=1;return;}
        Insert(node<<1|((num>>pos)&1), pos-1, num);
        cnt[node]=cnt[node<<1]+cnt[node<<1|1];
    }
    int Query(int node, int pos, int num){
        if(pos<0) return num;
        int cur=(sw>>pos)&1;
        cur|=node<<1;
        if(cnt[cur]<(1<<pos)) return Query(cur, pos-1, num);
        return Query(cur^1, pos-1,num+(1<<pos));
    }
}trie;
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    rep(i,0,n){
        int x;
        scanf("%d",&x);
        trie.Insert(1,19,x);
    }
    while(m--){
        int x;
        scanf("%d",&x);
        sw^=x;
        printf("%d\n",trie.Query(1,19,0));
    }
    return 0;
}

转载于:https://www.cnblogs.com/flipped/p/7456196.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值