CF1553H XOR and Distance

题目链接

Codeforces 1553H XOR and Distance

题目大意

给定一个长度为 \(n\) 的数列 \(a_i\)​ 和一个数字 \(k\),满足 \(a_i< 2^k\),设

\[f(x)=\min_{i=1}^n\min_{j=i+1}^n|(a_i\oplus x)-(a_j\oplus x)| \]

请对所有 \(0\leq x\leq 2^k-1\),求出 \(f(x)\)

\(1\leq k\leq 19\)\(2\leq n\leq2^k\)\(a_i\neq a_j\)

思路

建出字典树,可以发现答案即为字典树的所有相邻叶子的距离最小值,考虑 \(x\) 变化时 \(Trie\) 形态的变化,注意到如果 \(x\) 只翻转第 \(p\) 位,那么 \(Trie\) 的所有深度为 \(p-1\) 的节点的左右儿子就会发生互换,变化量是 \(O(2^{k-p})\) 的,所以如果我们可以保证第 \(i\) 位被翻转了 \(O(2^{k-i})\) 次,那么这边的时间复杂度就可以做到 \(1\times2^k+2\times2^{k-1}+...+2^k\times1=O(k\cdot2^k)\) 的了。

好像只要每个 \(x\)\(x\oplus highbit(x)\) 转移过来就可以满足这个条件了!但是这样好像不能一次做完,因为这个转移关系是一张 \(DAG\),还需要把 \(Trie\) 可持久化,看起来不好做。我们需要的是一个 \(0\)\(2^k-1\) 的排列,满足相邻两个数字只有一位被翻转了,且第 \(i\) 总共被翻转的次数是 \(O(2^{k-i})\) 的,第一个性质好像有点熟悉,这不就是格雷码嘛,手玩一下 \(CSP2019\) 给出的那个 \(Gray\;Code\) 构造,可以发现它是第 \(i\) 位被翻转 \(O(2^i)\) 次,这里把二进制位做一下镜像对称即可,即 \(FFT\) 里的那个 \(rev\) 数组。

时间复杂度对了,考虑具体维护信息,由于有左右儿子翻转这个操作,在 \(Trie\) 的每个节点上维护 \(ans,mx,mn,len\) 四个属性,分别表示当前子树内的答案,最大和最小值离左边界的距离(翻转后的),当前节点对应区间的长度,在 \(Trie\) 的形态变化时将信息上传即可。

维护是线性的,所以时间复杂度 \(O(k\cdot2^k)\)

Code

#include<iostream>
#include<stack>
#include<fstream>
#include<ctime>
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 600000
#define K 20
#define Inf 0x3f3f3f3f
using namespace std;

int Gray[N], rev[N], ans[N], Log[N];
int n, k;

struct Trie{
    struct node{
        int ans, c[2];
        int mn, mx, len;
    } t[N*K];
    int cnt;

    int New(int k){
        t[++cnt].ans = Inf;
        t[cnt].len = 1<<k, t[cnt].mn = Inf, t[cnt].mx = -1;
        return cnt;
    }
    void init(){ New(k), t[0].ans = Inf; }

    void update(int x){
        t[x].ans = min(t[t[x].c[0]].ans, t[t[x].c[1]].ans);
        t[x].mn = Inf, t[x].mx = -1;
        rep(i,0,1){
            int y = t[x].c[i];
            if(!y) continue;
            t[x].mn = min(t[x].mn, t[y].mn + i*t[y].len);
            t[x].mx = max(t[x].mx, t[y].mx + i*t[y].len);
        }
        int l = t[x].c[0], r = t[x].c[1];
        if(l && r) t[x].ans = min(t[x].ans, t[r].mn+t[l].len-t[l].mx);
    }

    void insert(int n){
        int x = 1;
        stack<int> s;
        per(i,k-1,0){
            s.push(x);
            int id = n>>i&1;
            if(!t[x].c[id]) t[x].c[id] = New(i);
            x = t[x].c[id];
        }
        t[x].mn = t[x].mx = 0;
        while(!s.empty()) update(s.top()), s.pop();
    }

    void change(int x, int lev, int p){
        if(x == 0) return;
        if(lev == p){
            swap(t[x].c[0], t[x].c[1]), update(x);
            return;
        }
        change(t[x].c[0], lev-1, p), change(t[x].c[1], lev-1, p);
        update(x);
    }

    void print(){
        rep(i,1,cnt) cout<<i<<":"<<t[i].c[0]<<","<<t[i].c[1]
            <<" "<<t[i].ans<<" "<<t[i].len<<' '<<t[i].mn<<","<<t[i].mx<<endl;
        cout<<endl;
    }
} Trie;

void init(){
    Gray[0] = 0, Gray[1] = 1;
    rep(i,1,k-1) rep(j,0,(1<<i)-1) 
        Gray[(2<<i)-j-1] = Gray[j]|(1<<i);
    rep(i,0,(1<<k)-1) rev[i] = rev[i>>1]>>1 | ((i&1)<<(k-1));
    rep(i,0,(1<<k)-1) Gray[i] = rev[Gray[i]];
    rep(i,0,k-1) Log[1<<i] = i;
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n>>k;
    int a;
    Trie.init();
    rep(i,1,n) cin>>a, Trie.insert(a);
    init();

    ans[0] = Trie.t[1].ans;
    rep(i,1,(1<<k)-1){
        Trie.change(1, k-1, Log[Gray[i]^Gray[i-1]]);
        ans[Gray[i]] = Trie.t[1].ans;
    }

    rep(i,0,(1<<k)-1) cout<<ans[i]<<" ";
    cout<<endl;
    return 0;
}
posted @ 2021-08-10 18:59  Neal_lee  阅读(75)  评论(0编辑  收藏  举报