Codeforces 948D Perfect Security 【01字典树】
<题目链接>
题目大意:
给定两个长度为n的序列,可以改变第二个序列中数的顺序,使得两个序列相同位置的数异或之后得到的新序列的字典序最小。
解题分析:
用01字典树来解决异或最值问题。因为是改变第二个序列的顺序,即按照第一个序列的顺序输出异或结果,所以我们将第二个序列建树。然后用第一个序列在树上进行查询。然后就是01字典树基本操作,因为每个数能够被使用一次,所以每个节点加上$num[N]$数组标记,并且加上del操作。
#include <bits/stdc++.h> using namespace std; const int N = 3e5+5; int n,pos=1; int nxt[N*30][2],val[N*30],num[N*30],A[N],P[N]; void Insert(int x){ //利用该数的二进制进行建树 int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(!nxt[now][to])nxt[now][to]=++pos; now=nxt[now][to]; num[now]++; } val[now]=x; } void del(int x){ //从Trie树上删除使用过的数 int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; now=nxt[now][to]; num[now]--; } } int query(int x){ int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(nxt[now][to] && num[nxt[now][to]])now=nxt[now][to]; //因为是使异或结果最小,所以尽可能与该数的二进制相同 else now=nxt[now][to^1]; } del(val[now]); return x^val[now]; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&A[i]); for(int i=1;i<=n;i++) scanf("%d",&P[i]),Insert(P[i]); for(int i=1;i<=n;i++) printf("%d ",query(A[i])); }
2019-03-02
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。