题目描述
SmallBell最近沉迷Lovelive,苦于[Beat In Angel]的fc。他在研究用什么队伍的时候,顺便把队伍的Cool属性也记录了下来,于是他得到 $n$ 个数。
旁边的WerKeyTom_FTD正苦于如何卡常数。他突然对SmallBell提了个问题:选择两个数 $i,j$ $(i eq j)$ ,把第 $i,j$ 个数异或起来,求第 $k$ 大的异或值是多少。注意: $(i,j)$ 和 $(j,i)$ 是不同方案。
SmallBell:好水啊,一下就切了!然后把问题交给了你。
数据范围
除 $k$ 外所有数为不大于 $5 imes 10^4$ 的正整数, $1 le k le n imes (n-1)$
题解
建立 $trie$ 树,考虑二分答案,然后每个数在 $trie$ 树上找出异或值 $≥mid$ 的个数总和,然后和 $k$ 判断即可,效率: $O(nlog^2n)$
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=5e4+5; int n,a[N],l,r=(1<<16)-1,d,t=1; LL k; struct O{ int ch[2],sz; }tr[N*50]; void ins(int x){ int p=1; for (int v,i=15;~i;i--){ v=(x>>i)&1; if (!tr[p].ch[v]) tr[p].ch[v]=++t; p=tr[p].ch[v];tr[p].sz++; } } int get(int x){ int p=1,sum=0; for (int v,i=15;~i;i--){ v=(x>>i)&1; if (d&(1<<i)) p=tr[p].ch[v^1]; else sum+=tr[tr[p].ch[v^1]].sz, p=tr[p].ch[v]; } return sum+tr[p].sz; } bool J(){ LL sum=0; for (int i=1;i<=n;i++) sum+=1ll*get(a[i]); return (sum>=k); } int main(){ scanf("%d%lld",&n,&k); for (int i=1;i<=n;i++) scanf("%d",&a[i]),ins(a[i]); while(l<r){ d=(l+r+1)>>1; if (J()) l=d; else r=d-1; } return printf("%d ",l),0; }