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

输入

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

输出

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

样例输入

4 5
1
1
3
4

样例输出

0 2 2 5 5

【样例解释】

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

题解

堆 + trie.
首先明确, trie树上可以找到一个数的xor第k大(小).
我们把每个数的异或第\(2\)小先装进堆里面(异或第\(1\)小是自己异或自己, 不在题目考虑范围内), 每次取出堆头, 假设堆头是第\(p\)小, 则插入第\(p + 1\)小即可.
注意应该取\(2k\)次, 因为每两次得到的结果是相同的.

#include <cstdio>
#include <cctype>
#include <queue>

namespace Zeonfai
{
	inline int getInt()
	{
		int a = 0, sgn = 1;
		char c;
		while(! isdigit(c = getchar()))
			if(c == '-')
				sgn *= -1;
		while(isdigit(c))
			a = a * 10 + c -'0', c = getchar();
		return a * sgn;
	}

	inline void print(int a)
	{
		if(! a)
			return;
		print(a / 10);
		putchar(a % 10 + '0');
	}

	inline void println(int a)
	{
		if(a < 0)
			putchar('-'), a *= -1;
		if(a == 0)
			putchar('0');
		print(a);
		putchar(' ');
	}
}

const int N = 100000;

struct trieTree
{
	struct node
	{
		node *suc[2];
		int cnt;

		inline node()
		{
			suc[0] = suc[1] = NULL;
			cnt = 0;
		}
	};

	node *rt;

	inline trieTree()
	{
		rt = new node;
	}

	inline void insert(int w)
	{
		node *u = rt;
		for(int i = 30; ~ i; -- i)
		{
			int k = w >> i & 1;
			if(u->suc[k] == NULL)
				u->suc[k] = new node;
			++ (u = u->suc[k])->cnt;
		}
	}

	inline int query(int w, int k)
	{
		node *u = rt;
		int res = 0;
		for(int i = 30; ~ i; -- i)
		{
			int tmp = w >> i & 1;
			if(u->suc[tmp] != NULL && u->suc[tmp]->cnt >= k)
				u = u->suc[tmp];
			else
				k -= u->suc[tmp] == NULL ? 0 : u->suc[tmp]->cnt, u = u->suc[tmp ^ 1], res += 1 << i;
		}
		return res;
	}
}trie;

struct record
{
	int id, k, w;

	inline record(int _id, int _k ,int _w)
	{
		id = _id, k = _k, w = _w;
	}

	inline int friend operator <(record a, record b)
	{
		return a.w > b.w;
	}
};

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("BZOJ3689.in", "r", stdin);
	#endif
	using namespace Zeonfai;
	int n = getInt(), k = getInt();
	static int a[N];
	for(int i = 0; i < n; ++ i)
		trie.insert(a[i] = getInt());
	static std::priority_queue<record> hp;
	for(int i = 0; i < n; ++ i)
		hp.push(record(i, 2, trie.query(a[i], 2)));
	for(int i = 0; i < k << 1; ++ i)
	{
		record res = hp.top();
		hp.pop();
		if(i & 1)
			println(res.w);
		if(res.k < n)
			hp.push(record(res.id, res.k + 1, trie.query(a[res.id], res.k + 1)));
	}
}
posted @ 2017-07-04 10:15  Zeonfai  阅读(133)  评论(0编辑  收藏  举报