题目链接:点击打开链接
题意:有一个串O,和一个串K每位异或后加密生成A,现在给重新排列的K以及A,求字典序最小的O
思路:因为给定的K是重新排列的,而现在要求字典序最小的O,贪心地,从第一个数字开始不断求满足条件的最小数字即可。
首先,正整数的异或操作是一个双射,即如果O^K = A,那么A^K = O,而要求最小的数字,则需要尽可能地使参加异或运算的两个数字二进制下每位数相同。
不妨对给定的串K的每个数字的二进制表示构造一棵Trie树,对于每一个串A的数字,依据其二进制表示沿着这棵树走,即可求得最小的对应的数。
AC代码如下:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
using namespace std;
#define FSIO ios::sync_with_stdio(0);cin.tie(0);
#define DEBUG(a) cout<<"DEBUG: "<<(a)<<endl;
const int MAXN = 2005;
const int MOD = 1e9+7;
const int INF = 1e9+7;
struct Trie{
struct Trie* next[2];
bool isend;
int cnt;
};
Trie* mytree;
int bnum[35];
int A[300005];
int N;
void buildTree(int* num)
{
if(mytree==NULL)
{
mytree = (Trie*)malloc(sizeof(Trie));
mytree ->next[0] = mytree->next[1] = NULL;
}
Trie* p = mytree;
Trie* tmp;
for(int i=30;i>=0;--i)
{
if(p->next[num[i]]==NULL)
{
tmp = (Trie*)malloc(sizeof(Trie));
tmp->isend = 0;
tmp->cnt = 1;
tmp->next[0] = tmp->next[1] = NULL;
p->next[num[i]] = tmp;
}
else
{
p->next[num[i]]->cnt++;
}
if(i==0) p->isend = 1;
p = p->next[num[i]];
}
}
int seekNum(int* num)
{
Trie* p = mytree;
int res = 0;
for(int i=30;i>=0;--i)
{
if(p->next[num[i]]!=NULL&&p->next[num[i]]->cnt!=0)
{
res += (1<<i)*num[i];
p->next[num[i]]->cnt--;
p = p->next[num[i]];
}
else
{
res += (1<<i)*(1-num[i]);
p->next[1-num[i]]->cnt--;
p = p->next[1-num[i]];
}
}
return res;
}
int main()
{
FSIO;
int tmp;
cin>>N;
for(int i=0;i<N;++i) cin>>A[i];
for(int i=0;i<N;++i)
{
memset(bnum,0,sizeof(bnum));
cin>>tmp;
for(int j=0;j<=30;++j)
{
if((tmp>>j)%2!=0) bnum[j]=1;
}
buildTree(bnum);
}
for(int i=0;i<N;++i)
{
memset(bnum,0,sizeof(bnum));
tmp = A[i];
for(int j=0;j<=30;++j)
if((tmp>>j)%2!=0) bnum[j]=1;
A[i] = A[i]^seekNum(bnum);
}
for(int i=0;i<N-1;++i) cout<<A[i]<<" ";
cout<<A[N-1]<<endl;
return 0;
}