CF1285D Dr. Evil Underscores 01Trie+贪心

给出 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) 1inmax(aiX),输出这个最小值. 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 1n105,0ai2301.
这道题直接去构造 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
#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();
}

你可能感兴趣的:(Codeforces,Trie,贪心)