3689: 异或之

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;
}
原文地址:https://www.cnblogs.com/mjtcn/p/10679617.html