[武汉加油] bzoj 3689: 异或之 Tire树
听说要建可持久化Trie树,但是我太 懒 菜了,所以自己就yy了一种不用可持久化的想法。
我们先建一棵Trie树,顺便记录一下树上节点的size,这样我们就能求出一个值和所有n个数异或起来后第k小。
我们维护一个优先队列,里面的元素hao表示数列中的第hao个数,val表示a[hao]和整个序列异或后的第js[hao]小的值。我们每次都从取top,再丢进去a[hao]和整个序列异或后的第js[hao]+1小的值。注意这样做点对(i,i)会被算进去,(i,j)和(j,i)都会算上,所以k=n+2*k;
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,k,cnt,root;
const int N=100010;
int a[N],tr[N*31][2],siz[N*31],js[N];
struct data
{
int hao,val;
friend bool operator <(const data &a,const data &b)
{
return a.val>b.val;
}
}tmp;
priority_queue<data>q;
inline int read()
{
int res = 0; char ch = getchar(); bool XX = false;
for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
return XX ? -res : res;
}
void Insert(int &k,int x,int t)
{
if(!k)k=++cnt;
if(!t){++siz[k];return;}
Insert(tr[k][(x>>(t-1))&1],x,t-1);
siz[k]=siz[tr[k][0]]+siz[tr[k][1]];
}
int ask(int k,int x,int t,int p)
{
if(!t)return 0;
if(p<=siz[tr[k][(x>>(t-1))&1]])return ask(tr[k][(x>>(t-1))&1],x,t-1,p);
else return (1<<(t-1))|ask(tr[k][!((x>>(t-1))&1)],x,t-1,p-siz[tr[k][(x>>(t-1))&1]]);
}
int main()
{
cin>>n>>k;k=n+k*2;
for(int i=1;i<=n;++i)Insert(root,a[i]=read(),30);
for(int i=1;i<=n;++i)q.push((data){i,ask(root,a[i],30,1)}),js[i]=1;
for(int i=1;i<=k;++i)
{
tmp=q.top();q.pop();
if(i>n&&((i-n)&1))cout<<tmp.val<<' ';
if(js[tmp.hao]==n)continue;
q.push((data){tmp.hao,ask(root,a[tmp.hao],30,++js[tmp.hao])});
}
return 0;
}