莫队二次离线
P4887 【模板】莫队二次离线:经典板子题,求区间内异或和在 进制下含 个 的二元组个数。
发现答案更新的极限复杂度大约为 ,均摊下来复杂度约为 ,总复杂度为 ,不太能接受。
考虑优化成 。
那么想到拆贡献。不妨设 表示 对 产生的贡献,设 表示 对 产生的贡献。
因为一个数 对区间的贡献具有可加 / 减性,所以 。
接下来就可以模拟指针移动到 的答案变化情况。
右指针移动到 时,。
左指针移动到 时,。
由于答案的可加 / 减性,我们可以把它变为 ,也就是做两次莫队进行统计。
当然,我们可以直接维护一个标记来记录其是加 / 减,然后把其离线下来,通过扫描线进行询问处理即可。
最后,由于我们进行的是单点修改,即最后答案是差分形式。做前缀和才能得到真实答案。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现