[BZOJ3261][最大异或和][可持久化Trie]

50 篇文章 0 订阅
5 篇文章 0 订阅

[BZOJ3261][最大异或和][可持久化Trie]

题目大意:

给定一个长度为 N 的序列正整数序列和M个操作,操作分为两种,其中操作 1 为在末尾添加一个数,操作2为求一个位置 p[L,R] ,使得 a[p]a[p+1]...a[N]x 最大并输出最大的数。

思路:

由于异或和这种东西满足区间加减的性质,不妨设 b[i] 为位置 i 的前缀异或和,所以[p,N]区间的异或和就相当于是 b[N]b[p1] ,而操作 2 就相当于是询问Max(b[p1]b[N]x),p[L,R]的最大值。

注意到除了 b[p1] 以外右边的值是唯一确定的,不妨设右边部分值为 s ,所以我们只需要维护一棵可持久化Trie(和可持久化线段树很类似),每次在root[L1] root[R] 之间查询 s 转为2进制后每一位取反是否存在,如果存在答案取这一位取反肯定是最优的,反之就取原来的这一位,然后向儿子递归,直到将s的二进制串在Trie里爬完为止。

代码:
#include <cstdio>                                                                                     
const int Maxn = 600005;
namespace IO {
    inline char get(void) {
        static char buf[1000000], *p1 = buf, *p2 = buf;
        if (p1 == p2) {
            p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
            if (p1 == p2) return EOF;
        }
        return *p1++;
    }
    inline void read(int &x) {
        x = 0; static char c;
        for (; !(c >= '0' && c <= '9'); c = get());
        for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
    }
    inline void write(int x) {
        if (!x) return (void)puts("0");
        if (x < 0) putchar('-'), x = -x;
        static short s[12], t;
        while (x) s[++t] = x % 10, x /= 10;
        while (t) putchar('0' + s[t--]);
        putchar('\n');
    }
    inline void get(char &x) {
        x = get();
        while (!(x >= 'A' && x <= 'Z')) x = get();
    }
};

int root[Maxn], a[Maxn], sum[Maxn], b[Maxn];
int n, m;

struct Trie {
    int cnt;
    int ch[Maxn << 5][2], sum[Maxn << 5];
    inline int insert(int x, int val) {
        int tmp, y;
        tmp = y = ++cnt;
        for (int i = 23; i >= 0; i--) {
            ch[y][0] = ch[x][0];
            ch[y][1] = ch[x][1];
            sum[y] = sum[x] + 1;
            int t = (val & b[i]) >> i;
            x = ch[x][t];
            ch[y][t] = ++cnt;
            y = ch[y][t];
        }
        sum[y] = sum[x] + 1;
        return tmp;
    }
    inline int query(int l, int r, int val) {
        int tmp = 0;
        for (int i = 23; i >= 0; i--) {
            int t = (val & b[i]) >> i;
            if (sum[ch[r][t ^ 1]] - sum[ch[l][t ^ 1]])
                tmp += b[i], r = ch[r][t ^ 1], l = ch[l][t ^ 1];
            else r = ch[r][t], l = ch[l][t];
        }
        return tmp;
    }
} trie;
int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    IO::read(n), IO::read(m); n++;
    b[0] = 1; for (int i = 1; i < 30; i++) b[i] = b[i - 1] << 1;
    for (int i = 2; i <= n; i++) IO::read(a[i]);
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] ^ a[i];
    for (int i = 1; i <= n; i++) root[i] = trie.insert(root[i - 1], sum[i]);
    char op;
    while (m--) {
        if (IO::get(op), op == 'A') {
            n++; IO::read(a[n]), sum[n] = sum[n - 1] ^ a[n];
            root[n] = trie.insert(root[n - 1], sum[n]);
        }
        else {
            int l, r, x;
            IO::read(l), IO::read(r), IO::read(x);
            IO::write(trie.query(root[l - 1], root[r], x ^ sum[n]));
        }
    }
    return 0;
}

完。

By g1n0st

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值