CF948D Perfect Security
题目链接:http://codeforces.com/contest/948/problem/D
知识点: Trie
题目大意:
给出两个长度为 \(N(1 \le N \le 300000)\) 的数组 \(A\) 和 \(P(0 \le A_{i},P_{i} \le 2^{30}\). 数组 \(P\) 可置换顺序,置换后得到数组 \(B(B_{i} = A_{i} \oplus P_{i})\),求能得到的字典序最小的数组 \(B\).
解题思路:
把数组 \(P\) 和 \(A\) 中的每一个数都视为带有前导零的 \(31\) 位二进制数。
将 \(P\) 中的每一个数由高位到低位插入字典树中。
依次遍历 \(A\) 中的每一个数,查询字典树中最适合与这个数异或的数(即二进制表示中不同的位最少并且高位尽可能相同的数),返回这个数(同时在字典树中将该数删除)。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 const int maxn = 300000+5; 5 int A[maxn]; 6 7 struct Trie{ 8 int ch[maxn*30][3]; 9 int val[maxn*30],nm[maxn*30]; 10 int sz; 11 Trie(){ 12 sz=1; 13 memset(ch[0],0,sizeof(ch[0])); 14 memset(val,0,sizeof(val)); 15 memset(nm,-1,sizeof(nm)); 16 } 17 void inserts(int num){ 18 int u=0; 19 for(int i=30;i>=0;i--){ 20 int nx; 21 if(num&(1<<i)) 22 nx=1; 23 else 24 nx=0; 25 if(!ch[u][nx]){ 26 memset(ch[sz],0,sizeof(ch[sz])); 27 ch[u][nx]=sz; 28 sz++; 29 } 30 u=ch[u][nx]; 31 val[u]++; //val 记录经过该结点的数字的个数 32 } 33 nm[u]=num; //nm 记录该叶子结点对应数字 34 } 35 int query(int num){ 36 int u=0; 37 for(int i=30;i>=0;i--){ 38 int nx; 39 if(num&(1<<i)) 40 nx=1; 41 else 42 nx=0; 43 if(ch[u][nx]&&val[ch[u][nx]]) 44 u=ch[u][nx]; //保证高位尽可能相同 45 else 46 u=ch[u][nx^1]; 47 val[u]--; 48 } 49 return nm[u]; 50 } 51 }; 52 Trie ac; 53 int main(){ 54 int N; 55 scanf("%d",&N); 56 for(int i=0;i<N;i++) 57 scanf("%d",&A[i]); 58 int num; 59 for(int i=0;i<N;i++){ 60 scanf("%d",&num); 61 ac.inserts(num); 62 } 63 for(int i=0;i<N;i++){ 64 if(i!=0) printf(" "); 65 printf("%d",ac.query(A[i])^A[i]); 66 } 67 return 0; 68 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”