[ABC308G] Minimum Xor Pair Query

思路

本题的线段树分治做法显然:维护每个元素存在的时间段,然后每进入线段树的一个节点时在 Trie 树上花 $\log V$ 的时间求出已有元素与它的最小异或值,放入一个堆中。离开一个节点时在堆中删除之前加入的最小异或值。

可删除堆实际上要用两个普通堆维护,代码如下:

struct Large_Root_Heap{
    priority_queue<ll> q, d; ll sz, s;
    void upd(){while(!d.empty() && q.top() == d.top()) d.pop(), q.pop();}
    ll top(){upd(); return q.top();}
    void ins(ll x){q.push(x), s += x, sz++;}
    void del(ll x){d.push(x), s -= x, sz--;}
} h[N];

另一个做法就是纯可重集做法,重在发现性质。

对于三个非负整数 $x,y,z(x < y < z)$,有 $\min(x \bigoplus y,y\bigoplus z)<x\bigoplus z$。

  • 证明

    从二进制最高位开始 $i=\log V$,对 $x,y,z$ 进行如下操作:

    1. 若它们的当前位都两两相同,继续跳到下一位 i--

    2. 根据三个数的大小关系,可以得到两种情况:

      1. $x_i=0,y_i=0,z_i=1$

      显然,这时有 $x\bigoplus y<x\bigoplus z$。

      1. $x_i=0,y_i=1,z_i=1$

      有 $y\bigoplus z<x\bigoplus z$。

有了上述性质,我们只需要维护大小相邻的数的异或和就行了。

开两个可重集。可重集 $s$ 记录黑板上当前有哪些元素;另一个可重集 $ans$ 记录 $s$ 中相邻元素的异或值。

插入删除操作在维护 $s$ 的同时维护一下 $ans$ 即可。

代码

#include <bits/stdc++.h>
#define L(i, a, b) for(int i = (a); i <= (b); i++)
#define R(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 3e5 + 10;
int q;
multiset<int> s, ans;
int main(){
    scanf("%d", &q);
    while(q--){
        int op, x; scanf("%d", &op);
        if(op == 1){
            scanf("%d", &x), s.insert(x);
            if(s.size() > 1){
                auto it = s.find(x), pre = it, nxt = it, it2 = s.end(); it2--;
                if(it == s.begin()) nxt++, ans.insert((*nxt) ^ x);
                else if(it2 == it) pre--, ans.insert((*pre) ^ x);
                else{
                    pre--, nxt++, ans.erase(ans.find((*pre) ^ (*nxt)));
                    ans.insert((*pre) ^ x), ans.insert((*nxt) ^ x);
                }
            }
        }
        if(op == 2){
            scanf("%d", &x);
            if(s.size() > 1){
                auto it = s.find(x), pre = it, nxt = it, it2 = s.end(); it2--;
                if(it == s.begin()) nxt++, ans.erase(ans.find((*nxt) ^ x));
                else if(it2 == it) pre--, ans.erase(ans.find((*pre) ^ x));
                else{
                    pre--, nxt++, ans.insert((*pre) ^ (*nxt));
                    ans.erase(ans.find((*pre) ^ x)), ans.erase(ans.find((*nxt) ^ x));
                }
            }
            s.erase(s.find(x));
        }
        if(op == 3) printf("%d\n", (*ans.begin()));
    }
    return 0;
} 
posted @ 2023-07-04 12:19  徐子洋  阅读(16)  评论(0)    收藏  举报  来源