「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\) 当前最高位,分情况讨论:
-
\(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\),此时我们不能确定大小,往下一个节点继续判断。 -
\(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;
}