Doubt [贪心, 0/1 Trie树]

D o u b t Doubt Doubt



最 初 想 法 \color{grey}{最初想法}

按顺序构造 c i c_i ci, 每次 贪心 地将 min ⁡ ( a ⨁ b ) \min(a \bigoplus b) min(ab) 取出, 作为 c i c_i ci, 可以保证字典序最小,
朴素实现是 O ( N 2 ) O(N^2) O(N2) 的, 加上用 对第二个测试点 相同的数字加速处理 可以获得 50 p t s 50pts 50pts .

考虑直接找每个 a i a_i ai 所匹配的 b i b_i bi, 发现若 a i a_i ai 找了 b i b_i bi, 会影响到后面的 a j a_j aj b i b_i bi, 换句话说, 这样会有后效性 .


正 解 部 分 \color{red}{正解部分}

不用关心 a i a_i ai 匹配哪个 b i b_i bi, 只需使得 c c c较小值较小 即可实现 字典序最小,

于是对 a a a b b b 分别开一个 深度越大, 位数越低 的 0 / 1   T r i e 0/1\ Trie 0/1 Trie树,
从两颗 T r i e Trie Trie贪心 地从根节点同步向下走, 优先走权值相同的节点, 使得 c c c越小值越小 即可 .


实 现 部 分 \color{red}{实现部分}

  • 根节点的下标赋为 1 1 1, 0 0 0 节点留作 虚点, 用来判边界 .
  • T r i e Trie Trie 中的 A d d ( ) Add() Add() 函数, t o to to 要开 b o o l bool bool 类型 .
#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 6e6 + 10;

struct Trie{

        int rot;
        int node_cnt;

        struct Node{ int ch[2], cnt; } T[maxn];

        void Add(int x){
                int cur = rot;
                for(reg int i = 30; i >= 0; i --){
                        bool to = (x & (1 << i));
                        if(!T[cur].ch[to]) T[cur].ch[to] = ++ node_cnt;
                        cur = T[cur].ch[to];
                        T[cur].cnt ++;
                }
        }

} t_a, t_b;

int c_cnt;
int c[maxn];

void DFS(int k_1, int k_2, int sum, int pw){
        if(k_1) t_a.T[k_1].cnt --, t_b.T[k_2].cnt --;
        if(pw == 0){ c[++ c_cnt] = sum; return ; }
        while(t_a.T[t_a.T[k_1].ch[0]].cnt && t_b.T[t_b.T[k_2].ch[0]].cnt) DFS(t_a.T[k_1].ch[0], t_b.T[k_2].ch[0], sum, pw>>1);
        while(t_a.T[t_a.T[k_1].ch[1]].cnt && t_b.T[t_b.T[k_2].ch[1]].cnt) DFS(t_a.T[k_1].ch[1], t_b.T[k_2].ch[1], sum, pw>>1);
        while(t_a.T[t_a.T[k_1].ch[1]].cnt && t_b.T[t_b.T[k_2].ch[0]].cnt) DFS(t_a.T[k_1].ch[1], t_b.T[k_2].ch[0], sum|pw, pw>>1);
        while(t_a.T[t_a.T[k_1].ch[0]].cnt && t_b.T[t_b.T[k_2].ch[1]].cnt) DFS(t_a.T[k_1].ch[0], t_b.T[k_2].ch[1], sum|pw, pw>>1);
}

int N;

int main(){
        N = read();
        t_a.rot = t_b.rot = 1;
        t_a.node_cnt = t_b.node_cnt = 1;
        for(reg int i = 1; i <= N; i ++) t_a.Add(read());
        for(reg int i = 1; i <= N; i ++) t_b.Add(read());
        DFS(1, 1, 0, 1<<30);
        std::sort(c+1, c+c_cnt+1);
        for(reg int i = 1; i <= c_cnt; i ++) printf("%d ", c[i]);
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值