Codeforces 923C - Perfect Security(01字典树)

在这里插入图片描述在这里插入图片描述

题目大意:

第一行输入一个 n ,表示有n个数,接下来输入两行,每行有n个数,暂且称这两行的数列为 a , b,你可以任意排列b数列,使得ai 和 bi 一一对应的位置上异或的值最小。输出异或后的结果。

解题思路:

01字典树的变种,这道题有这样几个点:既然是一一对应,那么b中的每个数只能用一次,匹配完了这个数就消失了,还有异或值最小,传统字典树是求异或最大,这时找的是和当前位上 &1 不一样的值使得异或最大,最小只需要使得从高位到地位每个二进制数尽可能相等即可。用一个cnt数组记录经过该节点的次数,每匹配一次cnt[ch[u][v]] 就-1,如果cnt[tot]的值 > 0 则证明可以进行匹配,再套用01字典树的模板即可。

Code:

#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstring>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 50;
int tot = 1;
ll val[N * 32], ch[N * 32][2], cnt[N * 32];
ll a[N];
void insert(ll x)
{
	int u = 0;
	for (int i = 31; i >= 0; i --)
	{
		int v = (x >> i) & 1;
		if (!ch[u][v])
		{
			ch[tot][0] = ch[tot][1] = 0;
			ch[u][v] = tot++;
		}
		u = ch[u][v];
		cnt[u] ++;//当前节点经过次数++
	}
	val[u] = x;
}
ll query(ll x)
{
	int u = 0;
	for (int i = 31; i >= 0; i --)
	{
		int v = (x >> i) & 1;
		if (ch[u][v] && cnt[ch[u][v]])  u = ch[u][v];//如果cnt值大于0则说明两位相同并且可以匹配
		else  u = ch[u][v ^ 1];
		cnt[u]--;//每次匹配完都要--
	}
	return val[u];
}
int main()
{
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++)
		cin >> a[i];
	for (int i = 1; i <= n; i ++)
	{
		ll s;
		cin >> s;
		insert(s);
	}
	for (int i = 1; i <= n; i ++)
	{
		ll res = query(a[i]);
		cout << (a[i] ^ res) << " ";
	}
	cout << endl;
	return 0;
}
posted @ 2020-08-03 12:20  Hayasaka  阅读(73)  评论(0编辑  收藏  举报