题解:P4887 【模板】莫队二次离线(第十四分块(前体))
学习了二次离线。
下文默认
题意自己看。
首先我们知道这个题是没有修改的,所以我们考虑莫队。
然而因为莫队加点删点的复杂度太高了(要在这个同时维护答案),所以这是过不去的。
回想莫队的过程,莫队分为两个部分,加点删点(下称修改),以及在插入的时候更新答案(下称查询)。
那么你会发现普通莫队是有
考虑莫队的本质,左右端点的移动对应了区间的拓展与收缩,考虑移动一个端点对答案的影响。
令
那么我们假设在莫队移动指针的时候是增加了右端点,那么我们就是要求出:
这就是从莫队的上一次的指针区间到这一次的指针区间答案的增量。
你发现这个题的贡献是可以差分的,所以你可以做一个转化,
你发现前半部分只和
然后后半部分,你发现这是一个点对一段前缀的贡献,考虑对前缀扫描线,然后在遍历所有点
剩下的三种莫队指针移动也是同理。
考虑复杂度分析,你发现我们的查询次数还是
现在就有不平衡的情况出现了,考虑用一些东西平衡复杂度,在别的二离题中常见的是用
对于本题,我们考虑一个异或的性质,
注意,我们莫队求得的是答案增量,所以最后要做一遍前缀和才能得到真正的答案。
令
那么总时间复杂度
还有一些细节见代码。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i , l, r) for(int i = l;i <= r;i++)
#define per(i , l , r) for(int i = l;i >= r;i--)
const int N = 1e5 + 5;
int len , a[N] , ct[N] , nw[N];
vector<int>tp;
struct sb{
int l, r , id , ans;
bool operator <(const sb &c) const{
return l / len ^ c.l / len ? l / len < c.l / len : r / len < c.r / len;
}
} q[N];
struct jb{
int l , r , id , fg , ii;
bool operator <(const jb&c) const{
return ii < c.ii;
}
};
int tott;
jb c[N << 2];
// vector<jb> c[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0) , cout.tie(0);
int n , m , k;
cin >> n >> m >> k;
rep(i , 0 , 16383) if(__builtin_popcount(i) == k) tp.push_back(i);
rep(i , 1 , n){
cin >> a[i];
nw[i] = ct[a[i]];
for(auto v : tp) ct[a[i] ^ v]++;
}
memset(ct , 0 , sizeof(ct));
rep(i , 1 , m) cin >> q[i].l >> q[i].r , q[i].id = i;
len = n / sqrt(m);
sort(q + 1 , q + m + 1);
int l = 1 , r = 0;
rep(i , 1 , m){
if(l > q[i].l) c[++tott] = {q[i].l , l - 1 , i , 1 , r};
while(l > q[i].l) q[i].ans -= nw[--l];
if(r < q[i].r) c[++tott] = {r + 1 , q[i].r , i , -1 , l - 1};
while(r < q[i].r) q[i].ans += nw[++r];
if(l < q[i].l) c[++tott] = {l , q[i].l - 1 , i , -1 , r};
while(l < q[i].l) q[i].ans += nw[l++];
if(r > q[i].r) c[++tott] = {q[i].r + 1 , r , i , 1 , l - 1};
while(r > q[i].r) q[i].ans -= nw[r--];
}
sort(c + 1 , c + tott + 1);
int jj = 1;
rep(i , 1 , n){
for(auto v : tp) ct[a[i] ^ v]++;
while(jj <= tott && c[jj].ii < i) jj++;
while(jj <= tott && c[jj].ii == i){
rep(j , c[jj].l , c[jj].r){
q[c[jj].id].ans += c[jj].fg * ct[a[j]];
if(j <= i && k == 0) q[c[jj].id].ans -= c[jj].fg;
}
jj++;
}
}
rep(i , 1 , m) q[i].ans += q[i - 1].ans;
vector<int>ans(m+1);
rep(i , 1 , m) ans[q[i].id] = q[i].ans;
rep(i , 1 , m) cout << ans[i] << '\n';
}
习题:P5047、P5398、P6778。
都是莫队二次离线的好题,值得一做。