莫队二次离线

P4887 【模板】莫队二次离线:经典板子题,求区间内异或和在 \(2\) 进制下含 \(k\)\(1\) 的二元组个数。

发现答案更新的极限复杂度大约为 \(\dfrac{n}{\log n}\),均摊下来复杂度约为 \(O(\log n)\),总复杂度为 \(O(N \sqrt N \log N)\),不太能接受。

考虑优化成 \(O(1)\)

那么想到拆贡献。不妨设 \(f(x,[l,r])\) 表示 \(a_x\)\([l,r]\) 产生的贡献,设 \(g(x,p)\) 表示 \(a_x\)\([1,p]\) 产生的贡献。

因为一个数 \(a_x\) 对区间的贡献具有可加 / 减性,所以 \(f(x,[l,r])=g(x,r)-g(x,l-1)\)

接下来就可以模拟指针移动到 \(x\) 的答案变化情况。

右指针移动到 \(x\) 时,\(\Delta=f(x,[l,x-1])=g(x,x-1)-g(x,l-1)\)

左指针移动到 \(x\) 时,\(\Delta=f(x,[x+1,r])=g(x,r)-g(x,x)\)

由于答案的可加 / 减性,我们可以把它变为 \(\sum g - \sum g'\),也就是做两次莫队进行统计。

当然,我们可以直接维护一个标记来记录其是加 / 减,然后把其离线下来,通过扫描线进行询问处理即可。

最后,由于我们进行的是单点修改,即最后答案是差分形式。做前缀和才能得到真实答案。

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

const int N = 1e5 + 10;
int n, m, k, siz, a[N], t[N]; ll pre[N], ans[N];

namespace fast_io {
  int it, ed, c, f, ot;
  char t, stk[20], bf[N + 50], ob[N + 50];
  #define gc (it == ed && (ed = (it = 0) + fread(bf, 1, N, stdin), it == ed))? EOF : bf[it++]
  template <typename T> inline void read(T &x) {
    x = 0; char ch = gc; int f = 1;
    for (; !isdigit(ch); ch = gc) if (ch == '-') f = -1;
    for (; isdigit(ch); ch = gc) x = x * 10 + (ch ^ 48); x *= f; return ;
  } template <typename T, typename ...Args>
  inline void read(T &x, Args &...args) { read(x), read(args...); }
  inline void fls() { fwrite(ob, 1, ot, stdout), ot = 0; }
  template <typename T> inline void write(T x, char opt) {
    while (x > 9) stk[++t] = 48 ^ (x % 10), x /= 10;
    for (ob[ot++] = 48 ^ x; t; ob[ot++] = stk[t--]);
    ob[ot++] = opt; if (ot > N) fls(); return ;
  }
} using fast_io::read; using fast_io::write;

struct query {
  int l, r, bel, id; ll res;
  inline bool operator <(const query &X) const {
    return bel != X.bel? l < X.l : (bel & 1? r < X.r : r > X.r);
  }
} q[N];

struct Node {
  int fi, se, th;
  Node(int fi, int se, int th):
    fi(fi), se(se), th(th) {}
}; vector <Node> g[N];

int main() {
  // freopen("P4887.in", "r", stdin);
  // freopen("P4887.out", "w", stdout);
  read(n, m, k); siz = sqrt(n);
  for (int i = 1; i <= n; ++i) read(a[i]);
  for (int i = 1; i <= n; ++i)
    read(q[q[i].id = i].l, q[i].r), q[i].bel = (q[i].l - 1) / siz + 1;
  if (k > 14) {
    for (int i = 1; i <= m; ++i) write(0, '\n');
    fast_io::fls(); return 0;
  }
  vector <int> tmp; tmp.clear();
  for (int i = 0; i < 16384; ++i)
    if (__builtin_popcount(i) == k) tmp.emplace_back(i);
  for (int i = 1; i <= n; ++i) {
    for (auto to: tmp) ++t[a[i] ^ to];
    pre[i] = t[a[i + 1]];
  }
  memset(t, 0, sizeof(t));
  int l = 1, r = 0;
  for (int i = 1; i <= m; ++i) {
    int lef = q[i].l, rig = q[i].r;
    if (l < lef) g[r].emplace_back(Node(l, lef - 1, -i));
    while (l < lef) q[i].res += pre[l - 1], ++l;
    if (l > lef) g[r].emplace_back(Node(lef, l - 1, i));
    while (l > lef) q[i].res -= pre[l - 2], --l;
    if (r < rig) g[l - 1].emplace_back(Node(r + 1, rig, -i));
    while (r < rig) q[i].res += pre[r], ++r;
    if (r > rig) g[l - 1].emplace_back(Node(rig + 1, r, i));
    while (r > rig) q[i].res -= pre[r - 1], --r;
  }
  for (int i = 1; i <= m; ++i) {
    for (auto to: tmp) ++t[a[i] ^ to];
    for (auto curr: g[i]) {
      int l = curr.fi, r = curr.se, id = curr.th;
      for (int j = l, tmp = 0; j <= r; ++j) {
        tmp = t[a[j]];
        if (j <= i && !k) --tmp;
        if (id < 0) q[-id].res -= tmp; else q[id].res += tmp;
      }
    }
  }
  for (int i = 1; i <= m; ++i) q[i].res += q[i - 1].res;
  for (int i = 1; i <= m; ++i) ans[q[i].id] = q[i].res;
  for (int i = 1; i <= m; ++i) write(ans[i], '\n');
  fast_io::fls();
  return 0;
}
posted @ 2022-10-19 10:46  MistZero  阅读(41)  评论(0编辑  收藏  举报