题目大意:给定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) ) ); } } }
时间: 2024-10-03 22:21:11