【可持久化0/1Trie】【P4735】最大异或和

Description

给定一个长度为 n 的序列 A,有 m 次操作,每次要么在序列尾部再添加一个数,将序列长度 n 加一,要么给进行一次查询,给定查询参数 l, r, x 要求在 [l, r] 内找一个位置 p,要求最大化 x  xor  Xori=pnAi

Limitation

1n, m3×105

0Ai107

Solution

考虑由于带加入,后缀异或和不太好维护,于是考虑维护前缀异或和 {presum},那么每次询问等于找一个点 p[l1,r),最大化 presump  xor  presumn  xor  x

由于 presumn  xor  x 是已知的,于是问题就是查询区间内一个位置最大化该点和某个给定值的异或值。 考虑类似主席树的建树方法建立可持久化0/1Trie,在 Trie 的每个节点记录前缀该节点共有几个为 0 的孩子和为 1 的孩子,相减即可维护对应区间每个节点是否有对应孩子。

有几个需要注意的细节,首先是由于查的是前缀-1,所以允许如果 l=1 则前缀什么都不选,因此 presum0 应插入一个 0 而不是置为空。另外注意通过查询的位置 xp1,对应相减的可持久化0/1Tire所维护的版本号应是 l2, r1。当 l=1 时需要特判维护版本号是 l1,r1,同时如果 r=1 则对应查询的版本号为 0, 0,当然什么也查不到,应该特判这种情况。另外由于插入 m 个数,原始 n 个数,所以数组需要开 n+m

Code

卡常的题都是屑题

出卡常题的出题人都是毒瘤出题人

好吧被卡的很难受,做了一点优化。

首先是注意到无论是插入还是查询,在0/1Trie上走的路径都是一条链而不具备分叉结构,所以不需要递归只需要循环即可解决问题。但是貌似意义不大,因为递归了写的也是尾递归,貌似开了 -O2 以后会被自动优化成循环233333所以并不能解决问题

写了个内存池,这个玩意真tm有用……甩动态分配内存两条街……

然后就最慢 940s 卡着过了

#include <cstdio>
#include <algorithm>

const int maxn = 600005;
const int upceil = 25;
const int tupceil = 24;
const int maxt = maxn * upceil;

struct Tree {
  Tree *ch[2];
  int v[2];

  Tree() { ch[0] = ch[1] = NULL; v[0] = v[1] = 0; }
};
Tree *rot[maxn], *pool[maxt], qwq[maxt];

int n, m, sum, top;
int MU[maxn];

void build(Tree *u, Tree *pre, const int v, int p);
int query(const Tree *const pre, const Tree *const u, const int v, const int p);

int main() {
  freopen("1.in", "r", stdin);
  qr(n); qr(m);
  for (int i = 0; i < maxt; ++i) pool[i] = qwq + i;
  build(rot[0] = pool[top++], NULL, 0, tupceil);
  for (int i = 1, x; i <= n; ++i) {
    x = 0; qr(x);
    build(rot[i] = pool[top++], rot[i - 1], sum ^= x, tupceil);
  }
  for (int a, b, c; m; --m) {
    a = 0;
    do a = IPT::GetChar(); while ((a != 'A') && (a != 'Q'));
    if (a == 'A') {
      a = 0; qr(a);
      build(rot[n + 1] = pool[top++], rot[n], sum ^= a, tupceil);
      ++n;
    } else {
      a = b = c = 0; qr(a); qr(b); qr(c);
      if (b == 1) {
        qw(sum ^ c, '\n', true);
        continue;
      }
      qw(query(rot[std::max(0, a - 2)], rot[b - 1], sum ^ c, tupceil) ^ sum ^ c, '\n', true);
    }
  }
  return 0;
}

void build(Tree *u, Tree *pre, const int v, int p) {
  while (~p) {
    if (pre) *u = *pre;
    int x = (v & (1 << p)) >> p;
    ++u->v[x];
    u = u->ch[x] = pool[top++];
    if (pre) pre = pre->ch[x];
    --p;
  }
}

int query(const Tree *pre, const Tree *u, const int v, int p) {
  int _ret = 0;
  while (~p) {
    int x = ((v & (1 << p)) >> p) ^ 1, y = u->v[x] - (pre ? pre->v[x] : 0);
    if (!y) x ^= 1;
    _ret |= (x << p);
    u = u->ch[x];
    if (pre) pre = pre->ch[x];
    --p;
  }
  return _ret;
}
posted @   一扶苏一  阅读(308)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2018-07-16 【单调队列】【P1714】 切蛋糕
点击右上角即可分享
微信分享提示