题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5536

题目:


Chip Factory

Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)

Problem Description

John is a manager of a CPU chip factory, the factory produces lots of chips everyday. To manage large amounts of products, every processor has a serial number. More specifically, the factory produces n chips today, the i-th chip produced this day has a serial number si.
At the end of the day, he packages all the chips produced this day, and send it to wholesalers. More specially, he writes a checksum number on the package, this checksum is defined as below:

maxi,j,k(si+sj)⊕sk
which i,j,k are three different integers between 1 and n. And ⊕ is symbol of bitwise XOR.

Can you help John calculate the checksum number of today?

Input

The first line of input contains an integer T indicating the total number of test cases.

The first line of each test case is an integer n, indicating the number of chips produced today. The next line has n integers s1,s2,..,sn, separated with single space, indicating serial number of each chip.

1≤T≤1000
3≤n≤1000
0≤si≤10^9
There are at most 10 testcases with n>100

Output

For each test case, please output an integer indicating the checksum number in a line.

Sample Input

2

3

1 2 3

3

100 200 300

Sample Output

6 400

 

 解题思路:


两层for遍历寻找i和j(i≠j),问题转化为01字典树的经典问题,在一堆数里找一个数s[k],使得s[k]和(s[i]+s[j])的异或值最大,但是题目又要求k,i,j互不相同,所以在i,j一定找k时,需要在01字典树上“删除”s[i],s[j]

但是并非真的的删除,exist[]记录每个节点在建01字典树时被访问的次数,当要删除s[i]时, 只需要把s[i]在树上的节点的exist值都-1,这样就实现了“删除”s[i]。后,+1 即恢复。

在树上遍历贪心寻找s[k]时,ch[u][v^1]节点不仅要在第一次建树时存在,而且要删除s[i]和s[j]之后也必须存在(exist值不为0)才能贪心的跳转到ch[u][v^1]去。

注:树上的一个节点在第一次建树的时候可能会被多次访问。

 

ac代码:


时间:近4s

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005;// 8!=40320!!不是10000
const int max_base = 32;
ll val[32*maxn], a[maxn];
int ch[32*maxn][2], exist[32*maxn];
int tol = 0;
void init()
{
    tol = 1;
    ch[0][0] =  ch[1][0] = 0;
}
void insert(ll x)
{
    int u = 0;
    for(int i = max_base; i >= 0; i--)
    {
        int v = (x >> i & 1);
        if(!ch[u][v]) // 新建节点
        {
            ch[tol][1] = ch[tol][0] = 0;
            val[tol] = 0; //不是数值节点
            exist[tol] = 0;//当前要确定的节点编号tol
            ch[u][v] = tol++;
        }
        u = ch[u][v];
        exist[u]++;//可能会被访问多次
    }
    val[u] = x;
}
void update(ll x, int add)
{
    int u = 0;
    for(int i = max_base; i >= 0; i--)
    {
        int v = x >> i & 1;
        u = ch[u][v];
        exist[u] += add;
    }
}
ll query(ll x)
{
    int u = 0;
    for(int i = max_base; i >= 0; i--)
    {
        int v = x >> i & 1;
        if(ch[u][v^1] && exist[ch[u][v^1]]) u = ch[u][v^1]; // 贪心的找每位异或起来的1的节点, 且这个节点存在,可能其他数在该节点上有相应的值
        else u = ch[u][v];
    }
    return  x^val[u];
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int t, n;
    scanf("%d", &t);
    while(t--)
    {
        init();
        scanf("%d", &n);
        ll ans = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%lld", &a[i]);
            insert(a[i]);
        }
        for(int i = 0; i < n; i++)
        {
            for(int j = i + 1; j < n; j++)
            {
                ll x = a[i] + a[j];
                update(a[i], -1);
                update(a[j], -1);//在01字典树上'删除'这两个数
                ans = max(ans, query(x));
                update(a[i], 1);
                update(a[j], 1);//恢复
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}