1. 程式人生 > 其它 >CF1553H XOR and Distance

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;
}