Codeforces 948D Perfect Security(字典树)

题目链接:Perfect Security

题意:给出N个数代表密码,再给出N个数代表key。现在要将key组排序,使key组和密码组的亦或所形成的组字典序最小。

题解:要使密码组里面每个数都找到能使其亦或和最小的数可以将key建成一棵字典树(这里建树方式很关键,可以每个数都从2^31开始建树,这样可以使我们在遍历树查询更加方便)。之后再遍历密码组每次在字典树里面找到一个能使它亦或和最小的数,再将这个数从字典树中删掉。。。  字典树太久不写,很多东西都忘记了!

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_N = 3e5+9;
 4 const int INF = 1e9+9;
 5 int tran[MAX_N*35][2]; //跳转到下一个节点的数组
 6 int sum[MAX_N*35],ans[MAX_N],A[MAX_N],P[MAX_N]; //sum - 记录每个节点记录的数的数量
 7 int N,M,T,S,siz;
 8 void init(){
 9     for(int i=0;i<MAX_N;i++){
10         tran[i][0] = tran[i][1] = 0;
11         sum[i] = 0;
12     }
13     siz = 0;
14 }
15 void _insert(int x){
16     int now = 0;
17     for(int i=31;i>=0;i--){ //从31开始就可以保证从高位开始建树了
18         int t = 0;
19         if(x & (1<<i)) t = 1;
20         if(!tran[now][t]) tran[now][t] = ++siz;   // 这里siz指的时节点的编号,这里如果下一个指向的节点没有的话就新生成一个节点给它。 
21         sum[tran[now][t]] ++; //节点上记录的数的数量加一
22         now = tran[now][t]; //跳转到下一个节点
23     }
24 }
25 void query(int id , int x){
26     int now = 0;
27     int res = 0;
28     for(int i=31;i>=0;i--){
29         int t =0;
30         if(x & (1<<i)) t = 1;
31         if(!sum[tran[now][t]]){
32             res += (1<<i);  
33             t = 1-t;
34         }
35         sum[tran[now][t]] --;
36         now = tran[now][t];
37 
38     }
39     ans[id] = res;
40 }
41 int main()
42 {
43     while(cin>>N)
44     {
45         init();
46         for(int i=0;i<N;i++){
47             scanf("%d",&A[i]);
48         }
49         for(int i=0;i<N;i++){
50             scanf("%d",&P[i]);
51             _insert(P[i]);
52         }
53         for(int i=0;i<N;i++){
54             query(i,A[i]);
55             cout<<ans[i]<<" ";
56         }
57         cout<<endl;
58 
59     }
60     return 0;
61 }
原文地址:https://www.cnblogs.com/doggod/p/9291063.html