BZOJ3689 异或之
题目描述:
给定n个非负整数A[1], A[2], ……, A[n]。
对于每对(i, j)满足1 <= i < j <= n,得到一个新的数A[i] xor A[j],这样共有n*(n-1)/2个新的数。求这些数(不包含A[i])中前k小的数。
题解:
建一棵01Trie树,然后把所有数扔进去。
将所有数的当前相关最小值插入优先队列,每次弹出来一个。
因为同一对会出现两次,所以我们只能选取奇数次弹出的点。
每弹出一个就将相关数的更小值插进去。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100050 #define ll long long inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,k,a[N],rt[N],sum; struct node { int x; ll v; node(){} node(int x,ll v):x(x),v(v){} friend bool operator < (node a,node b) { return a.v>b.v; } }; priority_queue<node>que; int dep[N]; struct Trie { int tot,siz[35*N],ch[35*N][2]; void insert(int x) { int u=1; for(int i=30;i>=0;i--) { int c = (x>>i)&1; siz[u]++; if(!ch[u][c])ch[u][c]=++tot; u=ch[u][c]; } siz[u]++; } ll query(int x,int k) { ll ret = 0; int u = 1; for(int i=30;i>=0;i--) { int c = (x>>i)&1; if(siz[ch[u][c]]>=k) { u=ch[u][c]; }else { ret|=(1ll<<i); k-=siz[ch[u][c]]; u=ch[u][!c]; } } return ret; } }tr; int main() { n=rd(),k=rd(); tr.tot=1; for(int i=1;i<=n;i++) { a[i]=rd(); tr.insert(a[i]); } for(int i=1;i<=n;i++) { dep[i]=2; ll tmp = tr.query(a[i],2); que.push(node(i,tmp)); } for(int i=1;i<=2*k;i++) { node tp = que.top(); que.pop(); if(i&1)printf("%lld ",tp.v); dep[tp.x]++; if(dep[tp.x]<=n) { ll tmp = tr.query(a[tp.x],dep[tp.x]); que.push(node(tp.x,tmp)); } } printf("\n"); return 0; }