题目链接
Codeforces 1553H XOR and Distance
题目大意
给定一个长度为 (n) 的数列 (a_i) 和一个数字 (k),满足 (a_i< 2^k),设
请对所有 (0leq xleq 2^k-1),求出 (f(x)) 。
(1leq kleq 19),(2leq nleq2^k),(a_i eq a_j)
思路
建出字典树,可以发现答案即为字典树的所有相邻叶子的距离最小值,考虑 (x) 变化时 (Trie) 形态的变化,注意到如果 (x) 只翻转第 (p) 位,那么 (Trie) 的所有深度为 (p-1) 的节点的左右儿子就会发生互换,变化量是 (O(2^{k-p})) 的,所以如果我们可以保证第 (i) 位被翻转了 (O(2^{k-i})) 次,那么这边的时间复杂度就可以做到 (1 imes2^k+2 imes2^{k-1}+...+2^k imes1=O(kcdot2^k)) 的了。
好像只要每个 (x) 从 (xoplus 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(kcdot2^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;
}