CF1553H XOR and Distance
题目链接
Codeforces 1553H XOR and Distance
题目大意
给定一个长度为 \(n\) 的数列 \(a_i\) 和一个数字 \(k\),满足 \(a_i< 2^k\),设
请对所有 \(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;
}