HDU 6625 three arrays(贪心+01字典树)

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6625

题意:给出一个 a a a数组和 b b b数组, a a a数组和 b b b数组一一异或得到 c c c数组,允许打乱 a a a数组和 b b b数组的顺序,使得得到的 c c c数组的字典序最小。

思路:我们可以对 a a a数组和 b b b数组分别建立两颗字典树。然后我们同步扫这两个字典树,如果分别到达了某一层的节点,如果在这个节点下都有往 0 0 0走的路,将两棵树同步向 0 0 0方向扫过去,如果都有往 1 1 1走的路,就将两棵树同步往 1 1 1走,(异或上相同的数字得到 0 0 0肯定是最优的),反之对应的位置只能一个为 0 0 0,一个为 1 1 1。然后按照这个策略同时走到底为进行一遍,得到一个解。然后我们只需要进行 n n n遍,就可以得到 n n n个解。

注意:
1.得到一个解的同时要删除得到这个解路径上的点,也就是删除对应的 a a a数组中的一个值和 b b b数组中的一个值。
2.最后的答案需要进行排序。因为虽然按照策略有 00 00 00 11 11 11就匹配,但是还会存在 01 , 10 01,10 0110的情况,你无法预料到这两种情况哪个的字典序小,所以最后的答案是需要排序的。

#include <bits/stdc++.h>

#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define CLR(x, y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 32 * 1e5 + 5;
const int mod = 1e9 + 7;


int T, n;
int a[maxn], b[maxn];
vector<int> ans;

int pos[2], trie[2][maxn][2], num[2][maxn], val[2][maxn];

/*
pos是节点标号,num[root]=x,表示到root节点的数字是x
val[root]=x,表示到节点root是几个数字的前缀
*/


void Insert(int id, int x) {//插入数字x,高位开始
    int root = 0;
    for (int i = 31; i >= 0; i--) {
        int t = (x >> i) & 1;
        if (!trie[id][root][t]) {
            trie[id][root][t] = ++pos[id];
        }
        root = trie[id][root][t];
        val[id][root]++;
    }
    num[id][root] = x;
}

bool check(int id, int root, int x) {
    return trie[id][root][x] && val[id][trie[id][root][x]];
}

int dfs() {
    int root0 = 0;
    int root1 = 0;
    for (int i = 31; i >= 0; i--) {
        if (check(0, root0, 0) && check(1, root1, 0)) {
            root0 = trie[0][root0][0];
            root1 = trie[1][root1][0];
            val[0][root0]--;
            val[1][root1]--;
        } else if (check(0, root0, 1) && check(1, root1, 1)) {
            root0 = trie[0][root0][1];
            root1 = trie[1][root1][1];
            val[0][root0]--;
            val[1][root1]--;
        } else if (check(0, root0, 0) && check(1, root1, 1)) {
            root0 = trie[0][root0][0];
            root1 = trie[1][root1][1];
            val[0][root0]--;
            val[1][root1]--;
        } else if (check(0, root0, 1) && check(1, root1, 0)) {
            root0 = trie[0][root0][1];
            root1 = trie[1][root1][0];
            val[0][root0]--;
            val[1][root1]--;
        }
    }

    return num[0][root0] ^ num[1][root1];

}

void Delete(int id, int x) {//删除数字x
    int root = 0;
    for (int i = 31; i >= 0; i--) {
        int t = (x >> i) & 1;
        root = trie[id][root][t];
        val[id][root]--;
    }
}


int main() {
    scanf("%d", &T);
    while (T--) {
        pos[0] = 0;
        pos[1] = 0;
        ans.clear();
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            Insert(0, a[i]);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &b[i]);
            Insert(1, b[i]);
        }

        for (int i = 1; i <= n; i++) {
            int x = dfs();
            ans.push_back(x);

        }
        sort(ans.begin(), ans.end());
        int sz = ans.size();
        for (int i = 0; i < sz - 1; i++) {
            printf("%d ", ans[i]);
        }
        printf("%d\n", ans[sz - 1]);

        for (int j = 0; j < 2; j++) {
            for (int i = 0; i < pos[j]; i++) {
                num[j][i]=0;
                trie[j][i][0]=0;
                trie[j][i][1]=0;
                val[j][i]=0;
            }
        }



    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值