Codeforces 282E(Sausage Maximization)

19 篇文章 0 订阅
16 篇文章 0 订阅
题目要求在给定序列中找到一个非空前缀和后缀,使得它们异或值最大。解决方案是先计算所有前缀和后缀的异或值,然后使用字典树在O(n)时间内找到不相交的前后缀,通过点贪心策略从高位开始匹配,以最大化异或结果。
摘要由CSDN通过智能技术生成

题意:给出一段序列,找出一个连续的前缀,一个连续的后缀,使这些数异或出来的值最大。除了前缀或后缀为空以外,前缀和后缀必须包含第一个和最后一个元素。

就是序列a1,a2,a3...an,找出前缀[0,l],后缀[r, n + 1],使这些数异或起来最大,其中r > l,a0和an+1假设它为0,就是说[0,0]表示前缀为空,[n+1,n+1]表示为后缀为空。

思路:这题有点神,没做过类似的题感觉有点难想出来,看了别人的解题报告之后发现,这题真的很神,忍不住把它写上了博客。首先可以将问题转化一下,可以发现序列的所有前缀和后缀都可以在O(n)的时间内求出来,然后问题就转化为了找出两个数异或起来最大,并且它们不能相交,异或起来最大就是尽量让这两个数的各个二进制位异或起来为1;接着字典树就派上用场了,首先我们枚举每一个后缀,在枚举的同时,将与其不相交的前缀插入到字典树中,并对于每一个后缀,我们可以求出和这个异或起来最大且不相交的前缀,至于求这个前缀,我们可以利用点贪心的思想,就是从高位开始枚举,就是越高位越能异或得到1就越好,这样我们就能求出这个前缀了。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 100010;
const int MAXBITS = 50;
LL num[N];

struct Node {
    int key;
    Node *child[2];
    Node(){ child[0] = child[1] = NULL; key = 0; }
} *root;

struct Trie {
    void insert(LL val) {
        Node *s = root;
        for(int i = MAXBITS; i >= 0; i--) {
            int dir = ((1LL << i) & val) > 0;
            if(s->child[dir] == NULL) {
                Node *p = new Node;
                p->key = dir;
                s->child[dir] = p;
            }
            s = s->child[dir];
        }
    }

    LL query(LL val) {
        LL res = 0;
        Node *s = root;
        for(int i = MAXBITS; i >= 0; i--) {
            int dir = ((1LL << i) & val) > 0;
            if(s->child[!dir] != NULL)
                s = s->child[!dir];
            else
                s = s->child[dir];
            res += (1LL << i) * s->key;
        }
        return res;
    }
} tree;

int main() {
    int n, i;
    cin >> n;
    LL prefix = 0, suffix = 0, ans = 0;
    for(i = 1; i <= n; i++) {
        scanf("%I64d", &num[i]);
        suffix ^= num[i];
    }
    for(i = 1; i <= n + 1; i++) {
        tree.insert(prefix);
        LL x = tree.query(suffix);
        ans = max(ans, (x ^ suffix));
        prefix ^= num[i], suffix ^= num[i];
    }
    cout << ans << endl;
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值