输入两个数组,可以将这两个数组顺序任意变化,问相同位置异或能生成的字典序最小序列。
一上来奇奇哥就告诉我这多半是01字典树,于是开始思考01字典树。
这题麻烦在于两个数组都能随意排列,一开始想到一个序列建01字典树,另一个序列在上面跑,仔细想了想发现这样要枚举一个数组的排列,铁超时。
自闭了一会儿想到干脆俩数组都建成01字典树然后用字典树跑字典树吧,敲的挺顺的一下就过了样例,3点提交了第一发,之后就wa到了比赛结束…赛后要了数据来测发现小样例完全正确,大样例输出就非常迷,才反应过来数组开小了orz,真实自闭,hdu不相信runtime error。
对于两个序列都建01字典树,之后贪心的dfs跑,如果两颗字典树都有相同的边,就往那边跑,如果实在没有,就只能跑不同的边。
赛后讨论的时候纠结了一下正确性(还不知道是数组开小了导致的wa,orz),思考过后发现是没有问题的,纠结的点在于这种做法要跑到最后才能得到一个值,也许先跑到的不是最小值,并且会导致最小值取不到。这种情况其实是不存在的,一般跑的时候,优先跑同边,这种时候可以保证取到的是最小值,如果两颗字典树在同一位置,都有0和1两个分叉,这种时候虽然确实不能保证先dfs到的是最小值,但是一个dfs的是0边,一个dfs的是1边,这两边完全不会相互影响。
最后也因为不能保证最小值先跑到,所以要排个序再输出。
orz,这辈子估计都不会再开小字典树了。
*2019多校第9场1008用下面这份代码改几行就ac了。
ac代码:
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int _, n, a[maxn], b[maxn];
struct Trie {
int next[maxn * 30][2];
bool end[maxn * 30];
int cnt[maxn * 30], sz, root;
int newNode() {
++sz;
memset(next[sz], 0, sizeof(next[sz]));
cnt[sz] = 0;
end[sz] = 0;
return sz;
}
void init() {
sz = 0;
root = newNode();
}
void add(int x) {
int p = root;
for (int i = 29, c; i >= 0; --i) {
c = (x >> i) & 1;
if (!next[p][c]) {
next[p][c] = newNode();
}
p = next[p][c];
++cnt[p];
}
end[p] = 1;
}
} A, B;
vector<pair<int, int>> ans;
void dfs(int p1, int p2, int x) {
int e = min(A.cnt[p1], B.cnt[p2]);
A.cnt[p1] -= e, B.cnt[p2] -= e;
if (A.end[p1]) {
ans.push_back({x, e});
return;
}
if ((A.cnt[A.next[p1][0]]) && (B.cnt[B.next[p2][0]])) {
dfs(A.next[p1][0], B.next[p2][0], x << 1);
}
if ((A.cnt[A.next[p1][1]]) && (B.cnt[B.next[p2][1]])) {
dfs(A.next[p1][1], B.next[p2][1], x << 1);
}
if ((A.cnt[A.next[p1][1]]) && (B.cnt[B.next[p2][0]])) {
dfs(A.next[p1][1], B.next[p2][0], (x << 1) + 1);
}
if ((A.cnt[A.next[p1][0]]) && (B.cnt[B.next[p2][1]])) {
dfs(A.next[p1][0], B.next[p2][1], (x << 1) + 1);
}
}
int main() {
scanf("%d", &_);
while (_--) {
scanf("%d", &n);
ans.clear();
A.init(), B.init();
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
A.add(a[i]);
}
for (int i = 0; i < n; ++i) {
scanf("%d", &b[i]);
B.add(b[i]);
}
dfs(A.root, B.root, 0);
sort(ans.begin(), ans.end());
for (int i = 0; i < ans.size(); ++i) {
for (int j = 0; j < ans[i].second; ++j) {
printf("%d", ans[i].first);
if (j < ans[i].second - 1)
printf(" ");
}
if (i < ans.size() - 1)
printf(" ");
}
printf("\n");
}
return 0;
}