杭电多校2019-5B-Three arrays(01trie+最优匹配)

博客讲述了如何利用01trie数据结构解决杭电多校2019比赛中关于Three arrays的问题。通过对a、b数组排序并寻找异或值最小的配对,实现c数组字典序最小。通过建立Trie树,搜索最优匹配并递归验证,最后输出最小字典序的c数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Three arrays

赛场上想了半年都没有一点思路。。。看了题解发现原来是我学过的东东。。。(标题我瞎编的名字)

题意:给了一个大小为1e51e51e5aaa数组和一个大小为1e51e51e5bbb数组,请你对a,ba,bab数组元素任意排序,使得到的ccc数组(ci=aic_i=a_ici=ai XorXorXor bib_ibi)字典序最小(其实不太准确)。

思路:

  1. 看到1e51e51e5 × 1e51e51e5,明显是要寻找一个能快速匹配的算法,但赛场上并没有想到,即使它很经典!
  2. 整体思路:对aia_iaibbb数组中寻找最优的bjb_jbj(对aia_iai来说异或后最小);然后检查对于bjb_jbj来说,aia_iaiaaa数组中是否是最优的,若也是最优的,那很显然,两情相悦;经过NNN次的两情相悦后,对他们得到的ccc数组排个序,就是答案了
  3. 那么如何在另一个数组中寻找对当前数最优的数呢?并且要对所有数都适用。
  4. 我们可以利用TrieTrieTrie树,先将所有的ai,bia_i,b_iai,bi都插入TrieTrieTrie树中(从二进制高位到低位),然后在寻找某一个数的最优匹配时,只需要尽可能沿着这个数在TrieTrieTrie树上的路径移动,这样异或出来的答案就会尽可能小
  5. 但由于aia_iaibjb_jbj来说可能还不是最优的,所以还需要询问一下aia_iaibjb_jbj是否是最优的,这样bjb_jbj会找到一个aka_kak,只需要看看aka_kak是否就是aia_iai;若是,那么就称他们为两情相悦,并且把他们从TrieTrieTrie树中删去(这样可以避免其他数对他们的痴心妄想);否则,用递归的思想再去判定bjb_jbj对刚刚找到的aka_kak是否是最优的,若是。。。否则。。。若。。。否。。。。。。
  6. 最后终于使NNN对数都匹配上了,然后只需要对他们的异或排个序输出就完事啦!

Three arrays

Time Limit: 3000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 557 Accepted Submission(s): 145

Problem Description
There are three integer arrays a,b,c. The lengths of them are all N. You are given the full contents of a and b. And the elements in c is produced by following equation: c[i]=a[i] XOR b[i] where XOR is the bitwise exclusive or operation.

Now you can rearrange the order of elements in arrays a and b independently before generating the array c. We ask you to produce the lexicographically smallest possible array c after reordering a and b. Please output the resulting array c.

Input

The first line contains an integer T indicating there are T tests.

Each test consists of three lines. The first line contains one positive integer N denoting the length of arrays a,b,c. The second line describes the array a. The third line describes the array b.

  • T≤1000

  • 1≤N≤105

  • integers in arrays a and b are in the range of [0,230).

  • at most 6 tests with N>100

Output

For each test, output a line containing N integers, representing the lexicographically smallest resulting array c.

Sample Input
1
3
3 2 1
4 5 6

Sample Output
4 4 7

#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 5e6+10;
const int mod = 1e9+7;
const double eps = 1e-9;

int N;
int nxt[maxn][2], cnt[maxn][2], sz, pair_cnt;
int c[maxn];

void add(int x, int f) { //按二进制插入Trie树中
    int now=0;
    for(int i=29; i>=0; --i) {
        int s=x>>i&1;
        if(!nxt[now][s]) nxt[now][s]=++sz;
        now=nxt[now][s];
        cnt[now][f]++;
    }
}

int choose(int f) { //随便写,能找到一个还没匹配的f(a或b)数组的数就行了
    int res=0, now=0;
    for(int i=29; i>=0; --i) {
        if(nxt[now][0]&&cnt[nxt[now][0]][f]) now=nxt[now][0];
        else res|=1<<i, now=nxt[now][1];
    }
    return res;
}

int choose_near(int x, int f) { //找另一个数组中最优的数
    int res=0, now=0;
    for(int i=29; i>=0; --i) {
        int s=x>>i&1;
        if(nxt[now][s]&&cnt[nxt[now][s]][!f]) {
            if(s) res|=1<<i;
            now=nxt[now][s];
        }
        else {
            if(!s) res|=1<<i;
            now=nxt[now][!s];
        }
    }
    return res;
}

void cut(int x, int f) { //删数
    int now=0;
    for(int i=29; i>=0; --i) {
        int s=x>>i&1;
        cnt[now=nxt[now][s]][f]--;
    }
}

bool choose_pair(int x, int f, int y) { //这里面的while(1)以及两个不同的return比较关键
    while(1) {
        int res=choose_near(x,f); //另一个数组中最自己最优的数
        if(res==y) { //两情相悦
            cut(x,f); //两个都删掉
            cut(y,!f);
            c[pair_cnt++]=x^y; //保存答案到c数组中
            return 1;
        }
        if(choose_pair(res,!f,x)) return 0;
    }
}

void init() {
    for(int i=0; i<=sz; ++i) cnt[i][0]=cnt[i][1]=nxt[i][0]=nxt[i][1]=0; //初始化多了要T哟
    sz=pair_cnt=0;
}

int main() {
	//ios::sync_with_stdio(false);
	int T=read();
    while(T--) {
        N=read();
        for(int i=0; i<N; ++i) add(read(),0); //按二进制插入Trie树中
        for(int i=0; i<N; ++i) add(read(),1);

        while(pair_cnt<N) {
            int x=choose(0); //这里随便找一个数就行了
            choose_pair(x,0,-1);
        }
        sort(c,c+N); //排个序输出答案咯
        for(int i=0; i<N; ++i) printf("%d%c", c[i], " \n"[i==N-1]);
        init();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值