【题意】给定长度为n的非负整数数组A和数组B,要求将数组B重排列使得A[i]^B[i]的字典序最小。n<=3*10^5,time=3.5s。
【算法】异或Trie
【题解】对一个数组O(n log n)建立异或Trie,就能O(log n)判断任意一个数在这个数组中异或值最大的数。
所以对B建异或Trie(每个数字从高二进制位开始插入),然后数组A依次在Trie上跑,从上到下尽量跑向相同数字边,这样得到字典序最小,路径中顺便删除标记。
复杂度O(n log n)。
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<queue> #include<stack> #include<set> #include<vector> #include<algorithm> #define ll long long #define lowbit(x) x&-x using namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } int min(int a,int b){return a<b?a:b;} int max(int a,int b){return a<b?b:a;} int ab(int x){return x>0?x:-x;} //int MO(int x){return x>=MOD?x-MOD:x;} //void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} /*------------------------------------------------------------*/ const int inf=0x3f3f3f3f,maxn=300010; int ch[maxn*30][2],sz,num[maxn*30*2],a[maxn]; int n; void insert(int y){ int x=0; for(int i=29;i>=0;i--){ bool k=(y&(1<<i))>0; if(!ch[x][k])ch[x][k]=++sz; num[ch[x][k]]++; x=ch[x][k]; } } int find(int y){ int x=0; for(int i=29;i>=0;i--){ bool k=(y&(1<<i))>0; if(num[ch[x][k]]>0){ x=ch[x][k]; num[x]--; y^=((k)<<i); } else{ x=ch[x][!k]; num[x]--; y^=((!k)<<i); } } return y; } int main(){ n=read(); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++)insert(read()); for(int i=1;i<=n;i++)printf("%d ",find(a[i])); return 0; }