CF1100F Ivan and Burgers

CF1100F Ivan and Burgers

静态区间,选取任意个数使得它们的异或和最大

\(n,\ m\leq5\times10^5,\ a_i\in[0,\ 10^6]\)

lxl ST表,线性基


如果暴力维护线性基,线段树时间复杂度为 \(O(n\log^2n)-O(\log^3n)\)

由于重复元素对答案没有影响,于是可以用 ST表 维护,时间复杂度为 \(O(n\log^3n)-O(\log^2n)\)

两种做法都无法通过本题。

如果沿用这个思路,瓶颈显然在线性基合并的 \(O(\log^2n)\) 上,无法再加优化

线段树做法显然无法再加拓展(和 lxl ST表时间复杂度一样的猫树空间复杂度多一只 \(\log\) ),于是考虑拓展 ST表 做法

常见的 RMQ 有 ST表 的 \(O(n\log n)-O(1)\) 的做法,但 \(O(n)-O(1)\) 的标准RMQ很难写、常数较大,且无法解决本题,于是可以考虑在随机数据下期望 \(O(n)-O(1)\) 的 lxl ST表

分块,大小设为 \(x\)

预处理每个块两端到块内每个点的前缀 \(\max\) 和后缀 \(\max\)

预处理块间ST表

若查询 \([l,\ r]\) ,且 \(l,\ r\) 分别在块 \(a,\ b\)

比如说我查l,r,这两个分别在块a,b中

则查块 \(a,\ b\) 之间的 RMQ ,以及 \(l\)\(a\) 块的后缀 \(\max\)\(r\)\(b\) 块的前缀 \(\max\)

\(l,\ r\) 在同一块中时,暴力求解

可以取 \(x=\log n\) ,当 \(l,\ r\) 不在同一块中时,这个算法是 \(O(1)\)

摘自 P3793 由乃救爷爷

如上维护,预处理块中前缀后缀线性基 \(O(n\log n)\) ,块间ST表线性基 \(O(\frac{n}{x}\log n\log^2n)=O(n\log^2n)\)

\(l,\ r\) 在同一块中,将 \([l,\ r]\) 中的元素暴力插入线性基, \(O(x\log n)=O(\log^2n)\) ;若 \(l,\ r\) 不在同一块中,合并前缀后缀块间三个线性基 \(O(\log^2n)\)

综上所述,时间复杂度 \(O(n\log^2n)\) ,空间复杂度 \(O(n\log n)\) 可能还需要卡卡常

代码

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

const int maxn = 5e5 + 10, maxm = 7850, base = 63;
int n, m, tot, lg[maxm], a[maxn];

#define get(x) (((x) + base) >> 6)

struct linear_base {
  int a[20];

  inline void clr() {
    memset(a, 0, sizeof a);
  }

  inline void ins(int x, int lim = 19) {
    for (int i = lim; ~i; --i) {
      if (x >> i & 1) {
        if (!a[i]) { a[i] = x; return; }
        x ^= a[i];
      }
    }
  }

  inline int query() {
    int res = 0;
    for (int i = 19; ~i; --i) {
      if ((res ^ a[i]) > res) res ^= a[i];
    }
    return res;
  }
} null, lef[maxn], rig[maxn], val[13][maxm];

inline linear_base operator + (linear_base A, const linear_base &B) {
  for (int i = 19; ~i; --i) {
    if (B.a[i]) A.ins(B.a[i], i);
  }
  return A;
}

const int maxn_r = maxn * 23, maxn_w = maxn * 8;
char buf_r[maxn_r], *now_r = buf_r;
char buf_w[maxn_w], *now_w = buf_w;

inline int read() {
  int x = 0;
  while (*now_r < 48) ++now_r;
  while (*now_r > 47) x = (x << 3) + (x << 1) + (*now_r ^ 48), ++now_r;
  return x;
}

inline void write(int x) {
  static char *tp, st[7];
  if (!x) *now_w = 48, ++now_w;
  for (tp = st; x; *++tp = x % 10 | 48, x /= 10);
  while (tp != st) *now_w = *tp, ++now_w, --tp;
  *now_w = 10, ++now_w;
}

inline linear_base query(const int &l, const int &r) {
  if (l > r) return null;
  const int k = lg[r - l + 1];
  return val[k][l] + val[k][r - (1 << k) + 1];
}

int main() {
  fread(buf_r, 1, maxn_r, stdin);
  n = read(), tot = get(n);
  for (int i = 1; i <= n; ++i) {
    a[i] = read();
    val[0][get(i)].ins(a[i]);
  }
  for (int i = 2; i <= tot; ++i) {
    lg[i] = lg[i >> 1] + 1;
  }
  linear_base lst;
  lst.clr();
  for (int i = 1; i <= n; ++i) {
    lst.ins(a[i]), lef[i] = lst;
    if (!(i & base)) lst.clr();
  }
  lst.clr();
  for (int i = n; i; --i) {
    if (!(i & base)) lst.clr();
    lst.ins(a[i]), rig[i] = lst;
  }
  for (int i = 1; i <= lg[tot]; ++i) {
    for (int j = 1; j + (1 << i) - 1 <= tot; ++j) {
      val[i][j] = val[i - 1][j] + val[i - 1][j + (1 << (i - 1))];
    }
  }
  m = read();
  register linear_base ans;
  for (int q = 1; q <= m; ++q) {
    const int l = read(), r = read();
    const int L = get(l), R = get(r);
    ans.clr();
    if (L == R) {
      for (register int i = l; i <= r; ++i) {
        ans.ins(a[i]);
      }
    } else {
      ans = rig[l] + lef[r] + query(L + 1, R - 1);
    }
    write(ans.query());
  }
  fwrite(buf_w, 1, now_w - buf_w, stdout);
  return 0;
}
posted @ 2019-05-16 20:26  cnJuanzhang  阅读(523)  评论(0编辑  收藏  举报