「EZEC-4」可乐

原题链接

Problem

给出长度为 \(n\) 的序列 \(a\) 以及一个 \(k\),让你求一个 \(x\),使得满足 \(a_i \oplus x \le k\) 的数量最多。
\(1 \le n,k,a_i \le 1\times 10^6\)

Solution

看到异或,我们就会想到 Trie 树,还有按位比较,事实上,这就是本题的核心。

复杂度分析

暴力枚举 \(x\),易知 \(1 \le x \le 2\times 10^6\approx 2^{21}\),然后我们用 Trie 树去找答案,总的复杂度为 \(O(10^6*21)\approx O(2*10^7)\)

解法

我们先把所有 \(a_i\) 丢进 Trie 树,然后我们把 \(x\)\(k\) 按位进行比较,找到符合条件的 \(a_i\)。令 \(c,t,v\) 分别为 \(x,k,a_i\) 当前最高位,分情况讨论:

  1. \(c=0,1\ \ \ t=1\)
    这时,如果我们取 \(a_i\) 当前位 \(v=c\),即使得 \(v \oplus c=0<t\),此时无论你后面怎么取,总有 \(a_i \oplus x<k\),所以我们加上符合条件的所有 \(a_i\),这个在建 Trie 树的时候预处理。
    然后如果我们取 \(v=c\oplus t\),即使得 \(v \oplus c =1=t\),此时我们不能确定大小,往下一个节点继续判断。

  2. \(c=0,1\ \ \ t=0\)
    这时,我们无法保证 \(v\oplus c<t\),只能取 \(v=c\oplus t\),使当前位相同,继续往下判断。

综上,我们查找 \(x\) 的答案时,如果有 \(t=1\),则加上符合条件的所有 \(a_i\),然后一直往下跳即可。

最后还有,如果找完了,那找到的肯定是相等的,还要加起来。
如果中途发生失配,那就直接退出,但不用加最后的。

具体参见代码。

Code

#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=3e6+7;
int mp[N][2],s[N],cnt,n,k,a[N],ans;
void add(int a)
{
	int p=0;
	for(int i=21;i>=0;i--)
	{
		int c=(a>>i)&1;
		if(!mp[p][c])mp[p][c]=++cnt;
		p=mp[p][c];s[p]++;//记录数量
	}
}
int find(int x)
{
	int p=0,ans=0,f=0;
	for(int i=21;i>=0;i--)
	{
		int c=(x>>i)&1,t=(k>>i)&1;
		if(t==1)ans+=s[mp[p][c]];//加上满足条件的
		if(!mp[p][c^t]){f=1;break;}
		p=mp[p][c^t];//一直跳
	}
	if(!f)ans+=s[p];
	return ans;
}
int main()
{
	read(n);read(k);
	for(int i=1;i<=n;i++)
		read(a[i]),add(a[i]);
	for(int i=0;i<=2097152;i++)
		ans=max(ans,find(i));
	cout<<ans;
	return 0;
}
posted @ 2022-07-16 15:27  Epoch_L  阅读(30)  评论(0编辑  收藏  举报