2019多校第五场 HDU6625 three arrays(贪心,带删除操作01字典树)

本文介绍了一种使用01字典树解决求两个序列异或后字典序最小序列的问题。通过构建两棵01字典树并遍历匹配异或值小的元素,实现序列的字典序最小化。

链接HDU6625 three arrays

题意:

给出长度为 n n n的两个序列 a a a b b b,对序列内随意排序, a [ i ] ⊕ b [ i ] = c [ i ]      ( 1 ≤ i ≤ n ) a[i]\oplus b[i]=c[i]\;\;(1\le i\le n) a[i]b[i]=c[i](1in),这样得到序列 c c c,要求得出字典序最小的序列 c c c

其中 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1n105 a , b ∈ [ 0 , 2 30 ) a,b\in[0,2^{30}) a,b[0,230)



分析:

要求字典序最小,那么只要贪心地从序列 a , b a,b a,b中尽可能把异或值小的匹配全部选出来,然后从小到大排个序就是答案,

这个就可以很好地用01字典树实现了,先把序列 a , b a,b a,b分别建成两棵01字典树,然后同时遍历,优先找同为0或同为1的(这样异或就可以尽可能地小),每次遍历完后删除这个值。

因为01字典树删除操作并不是真正的删除,而是利用一个记录访问次数结点次数的数组 n u m [ u ] num[u] num[u]( 详见->传送门 ),所以只要遍历的时候令 n u m [ u ] − 1 num[u]-1 num[u]1即可。



以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+50;
const int max_base=30;
int n;
struct bit_tire
{
    int ch[30*maxn][2],val[30*maxn],num[30*maxn],tot;

    void init()
    {
        tot=1;
        ch[0][0]=ch[0][1]=0;
        val[0]=0;
        num[0]=0;
    }

    void ins(int x)
    {
        int u=0;
        num[u]++;
        for(int i=max_base;i>=0;i--)
        {
            int c=(x>>i)&1;
            if(!ch[u][c])
            {
                ch[tot][0]=ch[tot][1]=0;
                val[tot]=0;
                num[tot]=0;
                ch[u][c]=tot++;
            }
            u=ch[u][c];
            num[u]++;
        }
        val[u]=x;
    }
}t[2];
bool check(int ua,int ub,int c1,int c2)  //子结点存在&&其num值>0
{
    return t[0].ch[ua][c1]&&t[0].num[t[0].ch[ua][c1]]&&t[1].ch[ub][c2]&&t[1].num[t[1].ch[ub][c2]];
}
int get_min()
{
    int ua=0,ub=0;
    for(int i=max_base;i>=0;i--)
    {
        if(check(ua,ub,0,0))       //同为0
        {
            ua=t[0].ch[ua][0];
            ub=t[1].ch[ub][0];
        }
        else if(check(ua,ub,1,1))  //同为1
        {
            ua=t[0].ch[ua][1];
            ub=t[1].ch[ub][1];
        }
        else if(check(ua,ub,0,1))  //0和1
        {
            ua=t[0].ch[ua][0];
            ub=t[1].ch[ub][1];
        }
        else                       //1和0
        {
            ua=t[0].ch[ua][1];
            ub=t[1].ch[ub][0];
        }
        t[0].num[ua]--;
        t[1].num[ub]--;
    }
    return t[0].val[ua]^t[1].val[ub];
}
int ans[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        t[0].init();
        t[1].init();
        scanf("%d",&n);
        int a,b;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            t[0].ins(a);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b);
            t[1].ins(b);
        }
        for(int i=1;i<=n;i++)
            ans[i]=get_min();
        sort(ans+1,ans+n+1);
        for(int i=1;i<=n;i++)
        {
            if(i>1)
                printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值