二次离线莫队
更新答案不是 \(O(1)\)?答案可差分?
二离来啦。
P4887 【模板】莫队二次离线
先考虑贡献:\(f(x,[l,r])\) 表示 \(x\) 对区间 \([l,r]\)。
考虑莫队每次的移动:\(r\to r'\)。答案增加为:
\[\sum_{i\in [r+1,r']} f(i,[l,i-1])=\sum_{i\in [r+1,r']} f(i,[1,i-1])-f(i,[1,l-1])
\]
发现前面那一坨可以直接预处理,莫队的时候 \(O(1)\) 更新即可。考虑后面。
设 \(g([r+1,r'],[1,l-1])\) 表示 \(\sum\limits_{i\in [r+1,r']} f(i,[1,l-1])=\sum\limits_{i<l} f(i,[r+1,r'])\)。
又,由莫队的复杂度证明知,所有 \([r+1,r']\) 的长度总和在线性根号级别。所以我们可以对 \(i\) 递增扫描线,每次更新前缀值,然后在 \([r+1,r']\) 暴力查询即可。
对于单点,查询怎么做?套路地开一个桶记录即可。单次复杂度为 \(O(\binom{14}{k})\),最大为 3000 多一点。
注意上面只列举了 \(r\to r'\) 的情况,其它情况是平凡的。
#include <bits/stdc++.h>
#define pb push_back
#define pii pair <int, int>
#define int long long
using namespace std;
const int N = 2e5 + 5; int read();
int n, m, k, pre[N], a[N], blo;
vector <int> ton; int b[N], ans[N]; // b
struct node {
int l, r, id;
friend bool operator < (node a, node b) {
if(a.l / blo == b.l / blo) return (a.l / blo) & 1 ? a.r < b.r : a.r > b.r;
return a.l < b.l;
}
} p[N];
struct sb {
int l, r, id, op;
sb (int a, int b, int c, int d) { l = a, r = b, id = c, op = d; }
};
vector <sb> G[N];
signed main() {
cin >> n >> m >> k; blo = sqrt(n);
for(int i = 1;i <= n; ++i) a[i] = read();
for(int i = 0;i < 16384; ++i) if(__builtin_popcount(i) == k) ton.pb(i);
for(int i = 1;i <= n; ++i) {
pre[i] = b[a[i]] + pre[i - 1];
for(int j : ton) b[j ^ a[i]] ++;
}
for(int i = 1;i <= m; ++i) p[i].l = read(), p[i].r = read(), p[i].id = i;
sort(p + 1, p + m + 1); int l = 1, r = 0;
for(int i = 1;i <= m; ++i) { // 顺序任意
int id = p[i].id, L = p[i].l, R = p[i].r;
// 删除
if(l < L) { // i[l, L-1] [i,r]
ans[id] += pre[L - 1] - pre[l - 1];
G[r].pb(sb(l, L - 1, id, -1));
l = L;
} if(r > R) { // i[R+1,r] [l,i]
ans[id] -= pre[r] - pre[R];
G[l - 1].pb(sb(R + 1, r, id, 1));
r = R;
}
// 加入
if(l > L) { // i[L, l-1] [i, r]
ans[id] -= pre[l - 1] - pre[L - 1];
G[r].pb(sb(L, l - 1, id, 1));
l = L;
} if(r < R) { // i[r+1, R] [l, i]
ans[id] += pre[R] - pre[r];
G[l - 1].pb(sb(r + 1, R, id, -1));
r = R;
}
}
memset(b, 0, sizeof b);
for(int i = 1;i <= n; ++i) {
for(int to : ton) b[to ^ a[i]] ++;
for(auto it : G[i]) {
int l = it.l, r = it.r, id = it.id, op = it.op;
for(int j = l;j <= r; ++j) ans[id] += op * (b[a[j]] - (k == 0 and j <= i));
}
}
for(int i = 1;i <= m; ++i) ans[p[i].id] += ans[p[i - 1].id];
for(int i = 1;i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}
int read() {
char c; int f = 1, sum = 0;
while(c < '0' or c > '9') {if(c == '-') f = -1;c = getchar();}
while(c >= '0' and c <= '9') {sum = (sum << 3) + (sum << 1) + (c ^ 48);c = getchar();}
return sum * f;
}