2019 Multi-University Training Contest 5补题

题目:hdu6625

题意:给你两个数组a,b,要求你重排a,b使c[i]=a[i]^b[i]得到的c数组字典序最小

分析:根据贪心思想,每次都要求得a[i]^b[i]都最小才能保证字典序最小。

有两种方法:

方法一:对a,b两个数组建两颗字典树,每次同时遍历两棵字典树,如果都为0或都为1就走,否则没有办法只有一个走0一个走1,此时对答案产生贡献,每次找出来的最后排个序就ok

Ac code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n;
int a[maxn],b[maxn];
struct Trie
{
    int ch[31*maxn][2],tot,num[31*maxn],val[31*maxn];
    void init(int n)
    {
        tot=0;
        for(int i=0; i<=30*n; i++)
        {
            num[i]=val[i]=0;
            for(int j=0; j<2; j++)
                ch[i][j]=0;
        }
    }
    void insert(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            if(!ch[rt][id])
                ch[rt][id]=++tot;
            rt=ch[rt][id];
            num[rt]++;
        }
        val[rt]=d;
    }
    int query(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            if(ch[rt][id]&&num[ch[rt][id]]) rt=ch[rt][id];
            else rt=ch[rt][id^1];
        }
        return val[rt];
    }
    void del(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            rt=ch[rt][id];
            num[rt]--;
        }
    }
} A,B;
int fnd()
{
    int p=0,q=0,ans=0;
    for(int i=30;i>=0;--i)
    {
        int id=0;
        if(A.ch[p][id]&&A.num[A.ch[p][id]]&&B.ch[q][id]&&B.num[B.ch[q][id]]) p=A.ch[p][id],q=B.ch[q][id];
        else if(A.ch[p][id^1]&&A.num[A.ch[p][id^1]]&&B.ch[q][id^1]&&B.num[B.ch[q][id^1]]) p=A.ch[p][id^1],q=B.ch[q][id^1];
        else if(A.ch[p][id]&&A.num[A.ch[p][id]]) ans|=(1<<i),p=A.ch[p][id],q=B.ch[q][id^1];
        else ans|=(1<<i),q=B.ch[q][id],p=A.ch[p][id^1];
    }
    A.del(A.val[p]);
    B.del(B.val[q]);
    return ans;
}
vector<int>ans;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ans.clear();
        scanf("%d",&n);
        A.init(n),B.init(n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]),A.insert(a[i]);
        for(int i=1; i<=n; i++)
            scanf("%d",&b[i]),B.insert(b[i]);
        for(int i=0; i<n; i++)
            ans.push_back(fnd());
        sort(ans.begin(),ans.end());
        for(int i=0;i<ans.size();i++)
           printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
    }
    return 0;
}

 

方法二:还有一种就是题解给的方法,确实挺nb的。

Ac code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n;
int a[maxn],b[maxn];
struct Trie
{
    int ch[31*maxn][2],tot,num[31*maxn],val[31*maxn];
    void init(int n)
    {
        tot=0;
        for(int i=0; i<=30*n; i++)
        {
            num[i]=val[i]=0;
            for(int j=0; j<2; j++)
                ch[i][j]=0;
        }
    }
    void insert(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            if(!ch[rt][id])
                ch[rt][id]=++tot;
            rt=ch[rt][id];
            num[rt]++;
        }
        val[rt]=d;
    }
    int query(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            if(ch[rt][id]&&num[ch[rt][id]]) rt=ch[rt][id];
            else rt=ch[rt][id^1];
        }
        return val[rt];
    }
    void del(int d)
    {
        int rt=0;
        for(int i=30; i>=0; --i)
        {
            int id=(d>>i)&1;
            rt=ch[rt][id];
            num[rt]--;
        }
    }
    int fnd()
    {
        int rt=0;
        for(int i=30;i>=0;--i)
        {
            int id=0;
            if(ch[rt][id]&&num[ch[rt][id]]) rt=ch[rt][id];
            else if(ch[rt][id^1]&&num[ch[rt][id^1]]) rt=ch[rt][id^1];
            else return -1;
        }
        return val[rt];
    }
} A,B;
vector<int>ans;
void solve()///flag=1表示u为A中的元素
{
    bool flag=1;
    stack<int>s;
    s.push(a[1]);
    while(!s.empty())
    {
        int u=s.top();
        s.pop();
        if(flag)
        {
            int v=B.query(u);
            if(!s.empty()&&v==s.top())
            {
                ans.push_back(u^v);
                B.del(v);
                A.del(u);
                s.pop();
            }
            else if(ans.size()==n-1)
            {
                ans.push_back(u^v);
                break;
            }
            else s.push(u),s.push(v),flag=!flag;
        }
        else
        {
            int v=A.query(u);
            if(!s.empty()&&v==s.top())
            {
                ans.push_back(u^v);
                A.del(v);
                B.del(u);
                s.pop();
            }
            else if(ans.size()==n-1)
            {
                ans.push_back(u^v);
                break;
            }
            else s.push(u),s.push(v),flag=!flag;
        }
        if(s.empty()){
            int d=A.fnd();
            if(d!=-1) s.push(d);
            flag=!flag;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ans.clear();
        scanf("%d",&n);
        A.init(n),B.init(n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]),A.insert(a[i]);
        for(int i=1; i<=n; i++)
            scanf("%d",&b[i]),B.insert(b[i]);
        solve();
        sort(ans.begin(),ans.end());
        //cout<<ans.size();
        for(int i=0; i<ans.size(); i++)
            printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值