正题
题目链接:https://www.luogu.com.cn/problem/CF1553H
题目大意
给出nnn个在[0,2n)[0,2^n)[0,2n)范围内的数字序列aaa。
对于每个x∈[0,2n)x\in[0,2^n)x∈[0,2n)求
mini≠j∣aixorx−ajxorx∣\min_{i\neq j}\ |a_i\ xor\ x-a_j\ xor\ x|i=jmin ∣ai xor x−aj xor x∣
2≤n≤2k,1≤k≤192\leq n\leq 2^k,1\leq k\leq 192≤n≤2k,1≤k≤19
解题思路
一个很妙的想法,考虑一个数字aaa与xxx有最多只有前ddd位不同的情况,记答案为fx,df_{x,d}fx,d。
考虑这个答案的性质,对于xxx找一个一个与它恰好第ddd位不同的yyy考虑转移,首先显然是从min{fx,d−1,fy,d−1}min\{f_{x,d-1},f_{y,d-1}\}min{fx,d−1,fy,d−1}处进行一个转移(因为只多了第ddd位可以不同,而此时这两种转移就包括了两个点集内的情况)。
但是还有一种转移有可能是在xxx的集合和yyy的集合中各自选一个数字,我们可以各自找到两个分别在x/yx/yx/y的集合中的数字aixorxa_i\ xor\ xai xor x最大并且ajxorya_j\ xor\ yaj xor y最小转移到xxx即可。
考虑如何快速找这两个数字,设mxx,d,mix,dmx_{x,d},mi_{x,d}mxx,d,mix,d表示上述所示情况下xxorax\ xor\ ax xor a的最大值/最小值,这两个的转移十分显然。
mxx,d=max{mxx,d−1,mxy,d−1+2d}mx_{x,d}=max\{mx_{x,d-1},mx_{y,d-1}+2^d\}mxx,d=max{mxx,d−1,mxy,d−1+2d}
mix,d=min{mix,d−1,miy,d−1+2d}mi_{x,d}=min\{mi_{x,d-1},mi_{y,d-1}+2^d\}mix,d=min{mix,d−1,miy,d−1+2d}
时间复杂度:O(2kk)O(2^kk)O(2kk)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1<<19;
int n,k,mx[N],mi[N],f[N];
int main()
{scanf("%d%d",&n,&k);memset(mx,0xcf,sizeof(mx));memset(mi,0x3f,sizeof(mi));memset(f,0x3f,sizeof(f));for(int i=1,x;i<=n;i++){scanf("%d",&x);mx[x]=mi[x]=0;}int MS=(1<<k);for(int i=0;i<k;i++){for(int s=MS-1;s>=0;s--)if((s>>i)&1){int t=s^(1<<i);f[s]=f[t]=min(f[s],f[t]);f[s]=min(f[s],mi[t]+(1<<i)-mx[s]);f[t]=min(f[t],mi[s]+(1<<i)-mx[t]);int mxt=mx[t],mit=mi[t];int mxs=mx[s],mis=mi[s];mx[s]=max(mx[s],mxt+(1<<i));mi[s]=min(mi[s],mit+(1<<i));mx[t]=max(mx[t],mxs+(1<<i));mi[t]=min(mi[t],mis+(1<<i));}}for(int i=0;i<MS;i++)printf("%d ",f[i]);return 0;
}