BZOJ3689. 异或之(01字典树第k小)

Description
给定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小的数。
注:xor对应于pascal中的“xor”,C++中的“^”。

Input
第一行2个正整数 n,k,如题所述。
以下n行,每行一个非负整数表示A[i]。

Output
共一行k个数,表示前k小的数。

Sample Input
4 5
1
1
3
4
Sample Output
0 2 2 5 5
Hint
【样例解释】

1 xor 1 = 0 (A[1] xor A[2])

1 xor 3 = 2 (A[1] xor A[3])

1 xor 4 = 5 (A[1] xor A[4])

1 xor 3 = 2 (A[2] xor A[3])

1 xor 4 = 5 (A[2] xor A[4])

3 xor 4 = 7 (A[3] xor A[4])

前5小的数:0 2 2 5 5

【数据范围】

对于100%的数据,2 <= n <= 100000; 1 <= k <= min{250000, n*(n-1)/2};

    0 <= A[i] < 2^31

Source

思路:
第k小要可持久化,听过没学过,这里的解法可以是维护01字典树每个节点出现次数,也算是一种套路了。

堆初始的时候插入每个数的第二小值,最小值为异或自己,求一个小值的方法就是尽量走和当前位相同的方向。

求第k小,要是和自己位相同的值出现次数x小于k,那么往这个位走最多是第k-1小了。这时候求相反位的第k - x小。

这种解法是维护01字典树每个节点出现次数,堆每次取出一个数再插入一个数的下一小值。

比较纠结的可能是要求2*k次。这是因为每个值会出现两次。
为什么?x可以是a ^ b,也可以是b ^ a。那么假设x是最小值先出队,那么下一个最小值肯定也是x,只不过是另一个数异或过来的。所以每个数会出堆两次。

如样例的出堆情况:
在这里插入图片描述

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 2e5 + 7;

int a[maxn],c[maxn * 31][2],siz[maxn * 31];
int tot = 1;

struct Node
{
    int v,k,a;
    bool operator < (const Node&rhs)const
    {
        return v > rhs.v;
    }
}nodes;

priority_queue<Node>q;

void insert(int x)
{
    int now = 1;
    for(int i = 30;i >= 0;i--)
    {
        int t = (x >> i) & 1;
        if(!c[now][t])
        {
            c[now][t] = ++tot;
        }
        now = c[now][t];
        siz[now]++;
    }
}

int query(int x,int k)
{
    int now = 1,ans = 0;
    for(int i = 30;i >= 0;i--)
    {
        int t = (x >> i) & 1;
        if(siz[c[now][t]] >= k)now = c[now][t];
        else k -= siz[c[now][t]],now = c[now][t ^ 1],ans += (1 << i);
    }
    return ans;
}

int main()
{
    int n,k;scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
        insert(a[i]);
    }
    for(int i = 1;i <= n;i++)
    {
        Node t;t.a = a[i];t.k = 2;
        t.v = query(a[i],2);
        q.push(t);
    }
    printf("\n");
    for(int i = 1;i <= 2 * k;i++)
    {
        Node t = q.top();q.pop();
        if(i & 1)printf("%d ",t.v);
        t.v = query(t.a,++t.k);
        q.push(t);
    }
    return 0;
}

发布了633 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/104092829