lightoj 1269 - Consecutive Sum(字典树)

Little Jimmy is learning how to add integers. As in decimal the digits are 0 to 9, it makes a bit hard for him to understand the summation of all pair of digits. Since addition of numbers requires the knowledge of adding digits. So, his mother gave him a software that can convert a decimal integer to its binary and a binary to its corresponding decimal. So, Jimmy's idea is to convert the numbers into binaries, and then he adds them and turns the result back to decimal using the software. It's easy to add in binary, since you only need to know how to add (0, 0), (0, 1), (1, 0), (1, 1). Jimmy doesn't have the idea of carry operation, so he thinks that

1 + 1 = 0

1 + 0 = 1

0 + 1 = 1

0 + 0 = 0

Using these operations, he adds the numbers in binary. So, according to his calculations,

3 (011) + 7 (111) = 4 (100)

Now you are given an array of n integers, indexed from 0 to n-1, you have to find two indices i j in the array (0 ≤ i ≤ j < n), such that the summation (according to Jimmy) of all integers between indices i and j in the array, is maximum. And you also have to find two indices, p q in the array (0 ≤ p ≤ q < n), such that the summation (according to Jimmy) of all integers between indices p and in the array, is minimum. You only have to report the maximum and minimum integers.

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 50000). The next line contains n space separated non-negative integers, denoting the integers of the given array. Each integer fits into a 32 bit signed integer.

Output

For each case, print the case number, the maximum and minimum summation that can be made using Jimmy's addition.

Sample Input

Output for Sample Input

2

5

6 8 2 4 2

5

3 8 2 6 5

Case 1: 14 2

Case 2: 15 1



观察题目中给的式子,会发现其实就是异或运算,就是让你求区间的异或和的最大值和最小值。

一看到求异或的最值问题,就知道要用字典树。用字典树可以解决这样的问题,就是给你一组数,让后再给你一个数,问你这个数和这组数中异或的最值是多少,你可以先把那一组数塞到字典树里,然后用给定的这个数去查询,如果要求最大值,就尽量往反方向走(如果是0,就往1走,如果是1,就往0走),如果求最小值,就往同方向走,这样就可以求得最值。

这题是让求一个区间的异或最值,我们可以转化成上述的给定一个数,在一组数中求得最值,我们可以先求出前缀异或和,塞进去一个前缀,查询一次,这样就可以得到区间异或的最值。举个例子说明一下。

sum[1] = a[1];

sum[2] = a[1] ^  a[2];

sum[3] = a[1] ^ a[2] ^a[3];

如果进行到查询sum[3]的时候,sum[3]就相当于给定的那个数,sum[1],sum[2]就相当于先前给的一组数,如果sum[3]和sum[1]的异或值是最值,那么就相当于[2,3]这个区间的异或值是最值,因为sum[3]^sum[1] = a[1] ^ a[2] ^a[3] ^ a[1] = a[2] ^a[3],一个数被异或了两次,就相当于没有。

后面的前缀异或和依次这样查询,就可以得到最值了。


#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;
struct trie
{
    trie *next[2];
    int cnt;
    trie()//构造函数,初始化数据
    {
        memset(next,0,sizeof(next));
        cnt=0;
    }
};
trie *root;
void insert(char *s,int x)//插入新的字符串
{
    trie *p=root;
    int i,k;
    for(i=31;i>=0;i--)
    {
        k=s[i]-'0';
        if(p->next[k]==NULL)
            p->next[k]=new trie();//不存在此节点则创建
        p=p->next[k];   //移向下一结点
    }
    p->cnt = x;
}
int query(char *s,int x,int dir)
{
    trie *p = root;
    for(int i=31;i>=0;i--)
    {
        int x = s[i] - '0';
        x = x^dir;
        if(p->next[x] == NULL)
            x^=1;
        p = p->next[x];
    }
    return (p->cnt) ^ x;
}
void del(trie *p)//释放内存
{
    for(int i=0;i<2;i++)
        if(p->next[i]!=NULL)
            del(p->next[i]);
    free(p);
}
void tran(char *a,int x)
{
    int i;
    for(i=0;i<=31;i++)
        a[i] = '0';
    a[i] ='\0';
    i = 0;
    while(x)
    {
        a[i++] = (x%2)+'0';
        x /= 2;
    }
}
const int inf = 1e9;
int main(void)
{
    int T,n,i,j;
    int sum[50010];
    scanf("%d",&T);
    int cas = 1;
    while(T--)
    {
        root=new trie();
        scanf("%d",&n);
        sum[0] = 0;
        for(i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            sum[i] = sum[i-1] ^ x;
        }
        char t[33];
        tran(t,0);//转化为字符串
        insert(t,0);//先把0塞到树里,任何数异或0都是它本身
        int maxn = 0,minn = inf;
        for(i=1;i<=n;i++)
        {
            tran(t,sum[i]);
            maxn = max(maxn,query(t,sum[i],1));
            minn = min(minn,query(t,sum[i],0));
            insert(t,sum[i]);
        }
        printf("Case %d: %d %d\n",cas++,maxn,minn);
        del(root);
    }

    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值