BZOJ 3689 异或之 Trie树+堆

题目大意:给定n个数,求这n个数两两异或的值中的前k小

首先我们对所有数字建立二进制Trie树,可以利用Trie树上的size域查询出一个数与其它数异或值的第k小

然后我们维护一个堆,将所有数与其它异或值的第2小加入堆(第一小是自己异或自己,不在题目要求范围内),当取出一个数异或值的第k小后,将第k+1小加入堆

一个异或值会被两个数分别取出一次,所以取出奇数次时输出,取2*k次即可

时间复杂度O(nlogn)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
using namespace std;
typedef pair<int, pair<int,int> > abcd;
struct Trie{
	int siz;
	Trie *son[2];
	Trie();
}*null=new Trie,*root=null;
Trie :: Trie()
{
	siz=0;
	son[0]=son[1]=null;
}
int n,a[M];
abcd heap[M];
int top;
void Push(abcd x)
{
	heap[++top]=x;
	int t=top;
	while( t>1 && heap[t]<heap[t>>1] )
		swap(heap[t],heap[t>>1]),t>>=1;
}
void Pop()
{
	heap[1]=heap[top--];
	int t=2;
	while( t<=top )
	{
		if( t<top && heap[t+1]<heap[t] )
			++t;
		if( heap[t]<heap[t>>1] )
			swap(heap[t],heap[t>>1]),t<<=1;
		else
			break;
	}
}
void Insert(Trie*&p,int x,int pos)
{
	if(p==null)	
		p=new Trie();
	p->siz++;
	if(!pos)
		return ;
	Insert(p->son[x&pos?1:0],x,pos>>1);
}
int Get_Kth(Trie*p,int x,int pos,int k)
{
	if(!pos)
		return 0;
	if(k<=p->son[x&pos?1:0]->siz)
		return Get_Kth(p->son[x&pos?1:0],x,pos>>1,k);
	else
		return Get_Kth(p->son[x&pos?0:1],x,pos>>1,k-p->son[x&pos?1:0]->siz)+pos;
}
int main()
{
	int i,k;
	cin>>n>>k;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]),Insert(root,a[i],1<<30);
	for(i=1;i<=n;i++)
		Push( make_pair( Get_Kth(root,a[i],1<<30,2) , make_pair(i,2) ) );
	for(i=1;i<=k<<1;i++)
	{
		abcd temp=heap[1];Pop();
		if(i&1)
			printf("%d ",temp.first);
		if(temp.second.second!=n)
		{
			int x=temp.second.first;
			int y=temp.second.second;
			Push( make_pair( Get_Kth(root,a[x],1<<30,y+1) , make_pair(x,y+1) ) );
		}
	}
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/PoPoQQQ/article/details/39992767
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞