给出
n
n
n个整数
a
1
,
a
2
,
…
,
a
n
a_1,a_2,\ldots,a_n
a1,a2,…,an,要求选出一个整数
X
X
X,最小化
max
1
≤
i
≤
n
(
a
i
⊕
X
)
\underset{1 \leq i \leq n}{\max} (a_i \oplus X)
1≤i≤nmax(ai⊕X),输出这个最小值.
1
≤
n
≤
1
0
5
,
0
≤
a
i
≤
2
30
−
1
1\leq n\leq 10^5,0\leq a_i\leq 2^{30}-1
1≤n≤105,0≤ai≤230−1.
这道题直接去构造
X
X
X,或者以
X
X
X为依据进行dp是有问题的(掉分的血泪教训,不再展开),而应该直接去贪心求这个最小值.
设最小值的答案是
A
n
s
Ans
Ans,对于第
i
i
i位,如果
n
n
n个数中第
i
i
i位为
0
0
0或
1
1
1都存在,那么
A
n
s
Ans
Ans就一定要加上
2
i
2^i
2i,也即
A
n
s
Ans
Ans的第
i
i
i位为
1
1
1.反之如果
0
0
0或
1
1
1有一个不存在,那么这一位取反就可使
A
n
s
Ans
Ans第
i
i
i位为
0
0
0.
因此我们可以建出Trie树,在树上从高位往低位进行贪心.对于第
i
i
i位的节点,如果左右儿子都有,这个节点答案就加上
2
i
2^i
2i,然后再对子树答案取
m
i
n
{\rm min}
min即可.时间复杂度就是Trie树的
O
(
n
log
m
a
x
a
i
)
O(n\log maxa_i)
O(nlogmaxai),但空间多大不是很确定…
总之对于这种可以看出有贪心性质的题,就不要在一棵树上吊死了吧
我 演 我 自 己
#include<bits/stdc++.h>
#define ll long long
#define clr(x,i) memset(x,i,sizeof(x))
using namespace std;
const int N=100005;
const ll mod=1e9+7;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,a[N],ch[N*21][2],tot;
void ins(int x)
{
int now=0;
for(int i=30; i>=0; i--){
bool t=(1<<i)&x;
if(!ch[now][t]){
ch[now][t]=++tot;
ch[tot][0]=ch[tot][1]=0;
}
now=ch[now][t];
}
}
int dfs(int now,int dep)
{
int k0=ch[now][0],k1=ch[now][1],ret=0;
if(!k0 && !k1) return 0;
if(!k0 && k1) {
int retR=dfs(ch[now][1], dep-1);
return retR;
}
if(k0 && !k1) {
int retL=dfs(ch[now][0], dep-1);
return retL;
}
ret+=(1<<dep)+min(dfs(ch[now][0], dep-1), dfs(ch[now][1], dep-1));
return ret;
}
void solve()
{
n=read();
for(int i=1; i<=n; i++){
a[i]=read();
ins(a[i]);
}
int ans=dfs(0, 30);
printf("%d\n",ans);
}
int main()
{
int T=1;
while(T--) solve();
}