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