BZOJ 3689 异或之

TRIE树+堆

如果只求最小的,我们可以建出TRIE树,对每一个数都跑一边求第二小(最小的是自己异或自己),这样一定包含了所有数的异或最小值,然后找最小的即可。

如果还需要求第二小,在排除第一小的情况下,我们把组成第一小的那个数在TRIE树上求一个第三小,这样同样依然包含了所有数的当前异或最小值,以此类推,堆维护即可。

注意a^b = b^a,也就是说一个值会出现两次,所以k应取k*2

#include<cstdio>
#include<queue>
#include<map>
#define N 100005
#define H 30
using namespace std;
int a[N], use[N];

map<pair<int,int>,bool> vis;

int nodecnt;
struct node
{
    node *ch[2];
    int siz;
    node(){ch[0]=ch[1]=NULL;siz=0;}
}s[N*50];

struct TRIE
{
    node *root;
    void init()
    {
        root = &s[++nodecnt];
    }
    void insert(int x)
    {
        node *p = root;
        for(int i = H, pos = 1<<H; ~i; i--, pos>>=1)
        {
            if(!p->ch[0])p->ch[0]=&s[++nodecnt];
            if(!p->ch[1])p->ch[1]=&s[++nodecnt];
            int son = x&pos?1:0;
            p=p->ch[son];
            p->siz++;
        }
    }
    int find(int x, int k)//第k小 
    {
        node *p = root;
        int ret = 0;
        for(int i = H, pos = 1<<H; ~i; i--, pos>>=1)
        {
            int son = x&pos?1:0;
            if(p->ch[son]->siz >= k)
            {
                ret<<=1;
                p=p->ch[son];
            }
            else
            {
                ret<<=1;
                ret|=1;
                k-=p->ch[son]->siz;
                p=p->ch[son^1];
            }
        }
        return ret;
    }
}trie;

struct number
{
    int x, v;
    number(int a, int b):x(a),v(b){}
    friend bool operator < (number a, number b)
    {
        return a.v>b.v;
    } 
};

int main()
{
    int n, k;
    scanf("%d%d",&n,&k);
    trie.init();
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);
        trie.insert(a[i]);
        use[i]=1;
    }

    priority_queue<number> q;

    for(int i = 1; i <= n; i++)
    {
        ++use[i];
        int num = trie.find(a[i],use[i]);
        q.push(number(i,num));
    }

    for(int i = 1, ii = 2*k; i < ii; i++)
    {
        number x = q.top(); q.pop();
        if(i&1)printf("%d ",x.v);
        int pos = x.x, num;

        if(use[pos]==n)continue;
        use[pos]++;
        num = trie.find(a[pos],use[pos]);
        q.push(number(pos,num));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值