题解:P11872 [威海市赛 2024] 异或盒子 1

这个 trick 是一个 dalao 告诉我的。

首先,我们从低位到高位建 trie。

对于每个节点,记录 \(xorv,cnt\)。其中 \(xorv\) 表示记录的异或和,\(cnt\) 表示当前节点被 \(cnt\) 个串共用(和普通的 trie 一样)。

维护异或和

首先应当清空本节点的 \(xorv\)

  • 如果有 \(0\) 儿子,异或上它的 \(xorv\)
  • 如果有 \(1\) 儿子,异或上它的 \(xorv\)
  • 如果有 \(1\) 儿子,且它的 \(cnt\)\(k\),那么对当前位的贡献为 \(w_1=\bigoplus\limits_{i=1}^k1=k\bmod 2\)

之所以不用考虑 \(0\) 儿子对这一位的贡献 \(w_0\),是因为 \(w_0=\bigoplus\limits_{i=1}^{k'}0=0\),相当于没有贡献。

全局加上一

OI Wiki 给出的例子。

1000(8)  + 1 = 1001(9)  ;
10011(19) + 1 = 10100(20) ;
11111(31) + 1 = 100000(32);
10101(21) + 1 = 10110(22) ;
100000000111111(16447) + 1 = 100000001000000(16448);

我们只需要从低位到高位开始找第一个出现的 \(0\),把它变成 \(1\),然后这个位置后面的 \(1\) 都变成 \(0\) 即可。

因此我们设计函数 \(add(x)\),然后对 \(1\) 儿子操作,并交换左右子树,达到进位,将 \(0\) 变成 \(1\),以及 \(1\) 变成 \(0\) 的效果。

void add(int id){
    if(!id)return;
    add(t[id].rs);//继续1的,0不
    swap(t[id].ls,t[id].rs);
    up(id);
}

至此,基本上做完了。

至于修改嘛,删掉这个数,然后再插入新的数就可以了,随便维护一下,记录一个全局增加量 \(Add\) 就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5*30+10;
struct node{
    int ls,rs,val,cnt;
}t[maxn];
int tot,root;
void up(int id){
    t[id].val=t[id].cnt=0;
    if(t[id].ls){
        t[id].val^=t[t[id].ls].val<<1;
        t[id].cnt+=t[t[id].ls].cnt;
    }
    if(t[id].rs){
        t[id].val^=t[t[id].rs].val<<1;
        t[id].cnt+=t[t[id].rs].cnt;
        if(t[t[id].rs].cnt%2)t[id].val|=1;
    }
}
void insert(int &id,int x,int dep){
    if(!id)id=++tot;
    t[id].cnt++;
    if(!dep)return;
    if(x%2==0)insert(t[id].ls,x>>1,dep-1);
    else insert(t[id].rs,x>>1,dep-1);
    up(id);
}
void erase(int id,int x,int dep){
    if(!id)return;
    t[id].cnt--;
    if(!dep)return;
    if(x%2==0)erase(t[id].ls,x>>1,dep-1);
    else erase(t[id].rs,x>>1,dep-1);
    up(id);
}
void add(int id){
    if(!id)return;
    add(t[id].rs);//继续1的,0不
    swap(t[id].ls,t[id].rs);
    up(id);
}
int query(){
    return t[root].val;
}
int v[maxn],Add;//v_x
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++)cin>>v[i],insert(root,v[i],20);
    while(q--){
        int op;
        cin>>op;
        if(op==0){
            int x,p;
            cin>>x>>p;
            erase(root,v[x]+Add,20);
            v[x]=p-Add;//这样下次查到p-Add+Add=p
            insert(root,p,20);
        }
        else if(op==1){
            add(root);
            Add++;
        }
        else{
            cout<<query()<<endl;
        }
    }
    return 0;
}

posted @ 2026-01-22 21:05  zhangruixiang  阅读(1)  评论(0)    收藏  举报