[武汉加油] 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;
}