hdu6625 2019杭电多校第五场

本文介绍了一个用于解决字典序最小配对问题的贪心算法,通过使用两棵Trie树来实现数字的高效匹配。该算法通过不断删除和查询,将两组数字进行配对,力求使得配对后的异或和最小,最终形成字典序最小的序列。

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

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6625

比赛的时候想出了一个和正解差不多的类似贪心,但是实现非常麻烦,而且最后半个小时实在是写不完,写到还剩10分钟的时候放弃了。

赛后打开dls直播间,看着dls画了张图,讲了一句话,秒懂,果然这种肯定是有一种很简单的贪心的,但感觉总离想到差一点。

首先考虑贪心目的,我们要字典序最小,那么就要小得尽量小,小的尽量多

建立tra,trb两棵trie,然后开始dfs,a1->bi->ai->bj->aj....bi是a1在trb中匹配出来的异或最小的,ai是bi在tra中匹配出来的使抑或和最小的,w1=bi^a1 ,w2=ai^bi  ,w3=bj^ai....,一定有w1<w2<w3...的关系。那么如果dfs到某个时刻  ai->bj->aj  , aj 与 ai 是同一个值,那么这个时候选择 aj 与 bj 配对一定没错,因为他们互为在对方集合中找出的异或最小值。

于是就是不停地删除和查询,n对数字配对完就行,复杂度O(n *30)

 其实还有很多其他的题,也可以用相同的贪心模型,就是两个集合配对,然后尽量小的问题,都可以这样去dfs贪心,成为一个2大小的环的时候就匹配成功,问题不是异或和都行。

#include<bits/stdc++.h>
#define maxl 100010
using namespace std;

int n,m,tota,totb,sum;
int ans[maxl],mi[30];
int a[maxl],b[maxl];
int suma[maxl*31],sumb[maxl*31];
int tra[maxl*31][2],trb[maxl*31][2];
int q[maxl],w[maxl];
vector <int> numa[maxl*31],numb[maxl*31];
vector <int> :: iterator it;
bool ina[maxl],inb[maxl];

inline void inserta(int id,int x)
{
    int u=0,c;
    for(int i=29;i>=0;i--)
    {
        suma[u]++;
        c=(x>>i)&1;
        if(!tra[u][c])
            tra[u][c]=++tota;
        u=tra[u][c];
    }
    suma[u]++;numa[u].push_back(id);
}

inline void insertb(int id,int x)
{
    int u=0,c;
    for(int i=29;i>=0;i--)
    {
        sumb[u]++;
        c=(x>>i)&1;
        if(!trb[u][c])
            trb[u][c]=++totb;
        u=trb[u][c];
    }
    sumb[u]++;numb[u].push_back(id);
}

inline void prework()
{
    for(int i=0;i<=tota;i++)
    {
        tra[i][0]=tra[i][1]=0;
        suma[i]=0;numa[i].clear();    
    }
    for(int i=0;i<=totb;i++)
    {
        trb[i][0]=trb[i][1]=0;
        sumb[i]=0,numb[i].clear();
    }
    scanf("%d",&n);
    tota=0;totb=0;
    for(int i=1;i<=n;i++)
    {    
        scanf("%d",&a[i]);
        inserta(i,a[i]);ina[i]=true;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        insertb(i,b[i]);inb[i]=true;
    }
}

inline int finda(int x)
{
    int u=0,c,tmp=0;
    for(int i=29;i>=0;i--)
    {
        c=(x>>i)&1;
        if(!tra[u][c])
            u=tra[u][c^1];
        else 
            u=tra[u][c];
    }
    it=numa[u].end();--it;
    return (*it);
}

inline int findb(int x)
{
    int u=0,c;
    for(int i=29;i>=0;i--)
    {
        c=(x>>i)&1;
        if(!trb[u][c])
            u=trb[u][c^1];
        else
            u=trb[u][c];
    }
    it=numb[u].end();--it;
    return (*it);
}

inline void dela(int x)
{
    int u=0,c,last;suma[u]--;
    for(int i=29;i>=0;i--)
    {
        c=(x>>i)&1;last=u;
        u=tra[u][c];
        suma[u]--;
        if(suma[u]==0)
            tra[last][c]=0;
    }
    it=numa[u].end();--it;
    numa[u].erase(it);
}

inline void delb(int x)
{
    int u=0,c,last;sumb[u]--;
    for(int i=29;i>=0;i--)
    {
        c=(x>>i)&1;last=u;
        u=trb[u][c];
        sumb[u]--;
        if(sumb[u]==0)
            trb[last][c]=0; 
    }
    it=numb[u].end();--it;
    numb[u].erase(it);
}

inline void dfs(int k)
{
    int id;
    if(k&1)
    {
        id=finda(b[q[k-1]]);
        if(id==q[k-2] && k-2>0)
        {
            delb(b[q[k-1]]);
            dela(a[id]);
            inb[q[k-1]]=false;
            ina[id]=false;
            ans[++ans[0]]=b[q[k-1]]^a[id];
            k-=2;sum--;
        }
        else
        {
            w[k]=b[q[k-1]]^a[id];
            q[k]=id;k++;
        }
        if(sum>0)
            dfs(k);
    }
    else
    {
        id=findb(a[q[k-1]]);
        if(id==q[k-2] && k-2>0)
        {
            dela(a[q[k-1]]);
            delb(b[id]);
            ina[q[k-1]]=false;
            inb[id]=false;
            ans[++ans[0]]=a[q[k-1]]^b[id];
            k-=2;sum--;
        }
        else
        {
            w[k]=a[q[k-1]]^b[id];
            q[k]=id;k++;
        }
        if(sum>0)
            dfs(k);
    }
}

inline void mainwork()
{
    ans[0]=0;sum=n;
    for(int i=1;i<=n;i++)
    if(ina[i])
    {
        q[1]=i;
        dfs(2);
    }
}

inline void print()
{
    sort(ans+1,ans+1+ans[0]);
    for(int i=1;i<=n;i++)
        printf("%d%c",ans[i],(i==n)?'\n':' ');
}

int main()
{
    mi[0]=1;
    for(int i=1;i<=29;i++)
        mi[i]=mi[i-1]*2;
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        prework();
        mainwork();
        print();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值