codevs 3031:最富有的人

题目描述 Description
在你的面前有n堆金子,你只能取走其中的两堆,且总价值为这两堆金子的xor值,你想成为最富有的人,你就要有所选择。

输入描述 Input Description
第一行包含两个正整数n,表示有n堆金子。
第二行包含n个正整数,表示每堆金子的价值。

输出描述 Output Description
第一行包含一个正整数,表示能获得的最大总价值。

样例输入 Sample Input
10
1 2 3 4 5 6 7 8 9 10

样例输出 Sample Output
15

数据范围及提示 Data Size & Hint
数据范围:
n<=100000 每堆金子数<=2^31-1
题目

  芒果君:这道题第一个难点在,你特么看不出它是trie树啊啊啊啊!

  然后,就算你看出来了,不会位运算,也hin难搞。

  过程描述起来很简单,就是把每个数字变成二进制从位数上限开始建立trie树,再逐个查询,尽量向“1”的方向走取最大更新答案。这里出现的几个位运算我弱弱的解释一下。

  ① (x>>i)&1 的作用是找到二进制下x从右到左的第i+1位,一个数“>>”右移i是除以2的x次方,“&”叫位与,同为1则为1,否则为0,用它可以找到二进制的每一位数,因为1&1=1,0&1=0;

  ② trie[p][t^1]  “^”叫异或,不同为1相同为0,你可以理解为“不进位的加法”,0^0=0,0^1=1,1^1=0,而且我才发现这道题用它找路的奇妙性质。我们的贪心策略是尽量往1走,如果该位是0需要1变成1,1需要0变成1,但是你有木有发现,它需要的“路”其实就是该位的数字与1异或的结果!

  ③total|=(1<<i)  左移“<<”就是乘2的i次方,“|”位或,只要有一个是1就是1,不然是0,它在这里可以强行把那一位改成1;

  

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define maxn 100010
 5 using namespace std;
 6 int trie[maxn<<6][3],cnt,n;
 7 long long a[maxn],ans;
 8 void insert(int x)
 9 {
10     int p=0;
11     for(int i=30;i>=0;--i){
12         int t=(x>>i)&1;
13         if(!trie[p][t]) p=trie[p][t]=++cnt;
14         else p=trie[p][t];
15     }
16 }
17 void query(int x)
18 {
19     int p=0;
20     long long sum=0; 
21     for(int i=30;i>=0;--i){
22         int t=(x>>i)&1;
23         if(!trie[p][t^1]) p=trie[p][t];
24         else{
25             p=trie[p][t^1];
26             sum|=(1<<i); 
27         }
28     }
29     ans=max(ans,sum);
30 }
31 int main()
32 {
33     scanf("%d",&n);
34     for(int i=1;i<=n;++i){
35         scanf("%lld",&a[i]);
36         insert(a[i]);
37     }
38     for(int i=1;i<=n;++i) query(a[i]);
39     printf("%lld
",ans);
40     return 0;
41 }

  (PS:好久没有这么认真的写过题解了……) 

原文地址:https://www.cnblogs.com/12mango/p/7241294.html