CF1285D Dr. Evil Underscores 01Trie+贪心

给出 n n 个整数 a 1 , a 2 , , a n a_1,a_2,\ldots,a_n ,要求选出一个整数 X X ,最小化 max 1 i n ( a i X ) \underset{1 \leq i \leq n}{\max} (a_i \oplus 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 .
这道题直接去构造 X X ,或者以 X X 为依据进行dp是有问题的(掉分的血泪教训,不再展开),而应该直接去贪心求这个最小值.
设最小值的答案是 A n s Ans ,对于第 i i 位,如果 n n 个数中第 i i 位为 0 0 1 1 都存在,那么 A n s Ans 就一定要加上 2 i 2^i ,也即 A n s Ans 的第 i i 位为 1 1 .反之如果 0 0 1 1 有一个不存在,那么这一位取反就可使 A n s Ans i i 位为 0 0 .
因此我们可以建出Trie树,在树上从高位往低位进行贪心.对于第 i i 位的节点,如果左右儿子都有,这个节点答案就加上 2 i 2^i ,然后再对子树答案取 m i n {\rm min} 即可.时间复杂度就是Trie树的 O ( n log m a x a i ) O(n\log maxa_i) ,但空间多大不是很确定…
总之对于这种可以看出有贪心性质的题,就不要在一棵树上吊死了吧
我 演 我 自 己

#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();
}
发布了39 篇原创文章 · 获赞 4 · 访问量 5726

猜你喜欢

转载自blog.csdn.net/Wolf_Reiser/article/details/103935306