3689: 异或之
分析:
01trie+堆。
首先考虑如何去一个数与其它数异或后的第k大,建出01trie,然后在trie上走,如果可以往小的边走,就往小的边走,否则往大的边。每个点记录下size,有多少个数。
查询一下每个数异或后最小的数,加入到堆中,不断删除最小的,加入与它异或下一小的。具体看代码理解。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cctype> #include<cmath> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 100005; struct Node { int x, k, id; Node() {} Node(int _x,int _k,int _id) { x = _x, k = _k, id = _id; } bool operator < (const Node &A) const { return x > A.x; } }; int ch[N * 30][2], siz[N * 30], a[N], Index; priority_queue< Node > q; void Insert(int x) { int u = 0; for (int i = 30; ~i; --i) { int c = (x >> i) & 1; if (!ch[u][c]) ch[u][c] = ++Index; u = ch[u][c], siz[u] ++; } } int getkth(int x,int k) { k ++; int res = 0, u = 0; for (int i = 30; ~i; --i) { int c = (x >> i) & 1; if (k <= siz[ch[u][c]]) u = ch[u][c]; else k -= siz[ch[u][c]], res |= (1 << i), u = ch[u][c ^ 1]; } return res; } int main() { int n = read(), k = read(); for (int i = 1; i <= n; ++i) { a[i] = read(); Insert(a[i]); } for (int i = 1; i <= n; ++i) { int x = getkth(a[i], 1); q.push(Node(x, 1, i)); } k <<= 1; for (int i = 1; i <= k; ++i) { Node now = q.top(); q.pop(); if (i & 1) printf("%d ", now.x); if (now.k != n - 1) q.push(Node(getkth(a[now.id], now.k + 1), now.k + 1, now.id)); } return 0; }