NEUOJ 711 (字典树)

题目链接:点击这里

Problem: 异星工厂

Time limit: 2s Mem limit: 1000 MB AC/Submission: 19/111 Discuss
Problem Description
在遥远的外太空,一位勇敢的探险家God Ma在宇宙中探索,不幸的是,他的飞船遭遇了太空风暴,飞船坠毁在了一个未知的星球。

由于没有足够的资源修复飞船,所以他需要采集一些资源,建造卫星,向他的小伙伴求救。

经过God Ma的探查,他找到了一条长度N为的资源带,资源带的每个点每秒都能产生一定量的资源物质Ai,资源物质需要用收集器来收集。

现在God Ma手里有两个收集器,每个收集器可以覆盖一个长度任意的区间,收集器的覆盖区间不能重叠,因为会发生碰撞。

虽然收集器的覆盖范围十分厉害,但是有一个缺点,在同一时刻收集器并不能收集所有的资源,只能收集到区间每个点资源量的异或值,若收集器覆盖的区间为[l,r],则一秒内能收集到的资源为(A[l] ^ A[l+1] …… ^ A[r])

由于God Ma想尽快发送求救信号,你能帮他计算出两个收集器每秒能够收集的资源之和最多是多少吗。

Input
多组数据。

第一行一个N(1 <= N <= 100000),表示资源带的长度

第二行N个数Ai(1 <= Ai <= 2^30),表示每个点上每秒产生的资源量

Output
每组数据输出一行,一个整数,表示两个收集器每秒能够收集的最大资源量。

Sample Input
5
1 2 3 1 2
Sample Output
6

题意:在一个数列中求两段使得每一段的异或再求和的结果最大.

很容易想到用前缀来搞,先把每一段的前缀异或和后缀异或,然后用字典树爬出每一个下标为结尾或者开头的最大异或子段的值,最后枚举分界点,在分界点左侧和右侧分别取一个最大的异或子段,更新最大值就好了.

#include <bits/stdc++.h>
using namespace std;
#define maxn 111111

int a[maxn], n;
struct node {
    int next[2];
}tree1[maxn*33], tree2[maxn*33];
int cnt1, cnt2;
int pre[maxn], la[maxn];
int l[maxn], r[maxn];
struct one {
    int num, id;
    bool operator < (const one &a) const {
        return num < a.num || (num == a.num && id < a.id);
    }
};

set <one> gg;
set <one>::iterator it;
int root1, root2;

int new_node1 () {
    cnt1++;
    tree1[cnt1].next[0] = tree1[cnt1].next[1] = -1;
    return cnt1;
}

int new_node2 () {
    cnt2++;
    tree2[cnt2].next[0] = tree2[cnt2].next[1] = -1;
    return cnt2;
}

void insert1 (int root, int num) {
    int p = root;
    for (int bit = 30, i = (1<<30); bit >= 0; i >>= 1, bit--) {
        int id = ((num&i)>0);//这一位是不是1
        if (tree1[p].next[id] == -1) {
            tree1[p].next[id] = new_node1 ();
        }
        p = tree1[p].next[id];
    }
}

void insert2 (int root, int num) {
    int p = root;
    for (int bit = 30, i = (1<<30); bit >= 0; i >>= 1, bit--) {
        int id = ((num&i)>0);//这一位是不是1
        if (tree2[p].next[id] == -1) {
            tree2[p].next[id] = new_node2 ();
        }
        p = tree2[p].next[id];
    }
}


int find1 (int root, int num) {
    int p = root;
    int ans = 0;
    for (int bit = 30, i = (1<<30); bit >= 0; i >>= 1, bit--) {
        int id = (((num&i)>0)^1);//这一位的值^1
        if (tree1[p].next[id] != -1) {
            ans = (ans^i);//这一位可以为1
            p = tree1[p].next[id];
        }
        else
            p = tree1[p].next[id^1];
    }
    return ans;
}

int find2 (int root, int num) {
    int p = root;
    int ans = 0;
    for (int bit = 30, i = (1<<30); bit >= 0; i >>= 1, bit--) {
        int id = (((num&i)>0)^1);//这一位的值^1
        if (tree2[p].next[id] != -1) {
            ans = (ans^i);//这一位可以为1
            p = tree2[p].next[id];
        }
        else
            p = tree2[p].next[id^1];
    }
    return ans;
}

int main () {
    while (scanf ("%d", &n) == 1) {
        for (int i = 1; i <= n; i++) {
            scanf ("%d", &a[i]);
        }
        gg.clear ();
        pre[0] = 0;
        for (int i = 1; i <= n; i++) {
            pre[i] = pre[i-1]^a[i];
        }
        la[n+1] = 0;
        for (int i = n; i >= 1; i--) {
            la[i] = la[i+1]^a[i];
        }
        cnt1 = cnt2 = 0;
        root1 = new_node1 ();
        root2 = new_node2 ();
        insert1 (root1, 0), insert2 (root2, 0);
        for (int i = 1; i <= n; i++) {
            int num = find1 (root1, pre[i]);
            l[i] = num;
            insert1 (root1, pre[i]);
        }
        for (int i = n; i >= 1; i--) {
            int num = find2 (root2, la[i]);
            r[i] = num;
            insert2 (root2, la[i]);
        }
        for (int i = 2; i <= n; i++) {
            l[i] = max (l[i], l[i-1]);
        }
        for (int i = n-1; i >= 1; i--) {
            r[i] = max (r[i], r[i-1]);
        }
        long long ans = l[-1];
        for (int i = 1; i < n; i++) {
            ans = max (ans, 1LL*l[i]+r[i+1]);
        }
        printf ("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值