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;
}

 

转载于:https://www.cnblogs.com/LiGuanlin1124/p/10046267.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值