洛谷 P4887 -【模板】莫队二次离线(第十四分块(前体))(莫队二次离线)
首先看到这类数数对的问题可以考虑莫队,记 \(S\) 为二进制下有 \(k\) 个 \(1\) 的数集,我们实时维护一个桶 \(cnt_i\) 表示当前区间中值为 \(i\) 的数有多少个,那么加入一个数 \(v\) 的时候,答案会增加 \(\sum\limits_{y\in S}cnt_{y\oplus v}\),这样暴力莫队复杂度是 \(n\sqrt{n}\dbinom{14}{k}\),如果你过了我请你吃糖
考虑优化,我们记 \(f(x,l,r)\) 表示当前区间为 \([l,r]\),加入 \(a_x\) 后答案的增量,记 \(\sum\limits_{y\in S}cnt_{y\oplus a_x}\)。考虑莫队中每个端点的移动对答案的贡献,我们以右端点为例,设右端点由 \(r\) 移动到了 \(r'\),这里我们不妨设 \(r'>r\),\(r'<r\) 的情况也同理,那么答案显然会增加 \(\sum\limits_{i=r+1}^{r'}f(i,l,i-1)\),这东西不太好直接求,考虑用差分的思想,将这东西拆成 \(f(i,1,i-1)-f(i,1,l-1)\),那么 \(\Delta=\sum\limits_{i=r+1}^{r'}f(i,1,i-1)-\sum\limits_{i=r+1}^{r'}f(i,1,l-1)\),不难发现第一个 \(\sum\) 里的东西只与 \(i\) 有关,我们可以预处理 \(f(i,1,i-1)\) 的前缀和即可 \(\mathcal O(1)\) 求出,至于怎么求 \(f(i,1,i-1)\)……这个看不出来就有点【数据删除】了罢,就从左往右扫一遍并实时维护一个桶 \(cnt\),扫到 \(a_i\) 的时候 \(f(i,1,i-1)\) 的值就是当时 \(\sum\limits_{x\in S}cnt_{x\oplus a_i}\)。后面那坨东西在线求出不太容易,不过既然叫“莫队二次离线”那就离线一下呗,不难发现所有这样的东西都可以用一个三元组 \((x,l,r)\) 表示 \(\sum\limits_{i=l}^rf(i,1,x)\),我们将这样的询问都挂在 \(x\) 上然后从左往右扫描一遍,还是实时维护一个桶 \(cnt\),不过这时候 \(cnt_v\) 的含义为 \([1,i]\) 中有多少个数 \(j\) 满足 \(v\oplus a_j\in S\),这显然可以在 \(\mathcal O(n\dbinom{14}{k})\) 的时间内求出,然后每遇到一个询问 \((i,l,r)\),就暴力枚举 \(t\in[l,r]\) 并令贡献加上/减去 \(cnt_{a_t}\),由于莫队指针移动的总距离为 \(n\sqrt{n}\) 级别的,因此这里的 \(\sum r-l+1\) 也是 \(n\sqrt{n}\) 级别的,可以通过。
上文所叙述的都是右端点移动的情况,左端点移动的情况大体上也差不多,还是设左端点由 \(l\) 移动到了 \(l'\),\(l'<l\)那么答案的增量就是 \(\sum\limits_{i=l'}^{l-1}f(i,i+1,r)\),这里有一个小小的区别就是不能差分前缀和,而要差分后缀和,即 \(f(i,i+1,r)=f(i,i+1,n)-f(i,r+1,n)\),这两项都可以用类似的方式维护。
最后,由于我们求出的是每次莫队后答案的增量,还需对每次询问的结果求一遍前缀和即可得到每次询问真正的答案。
时间复杂度 \(n\sqrt{n}+n\dbinom{14}{k}\)
最后总结一下莫队二次离线的适用范围:
- 可以莫队
- 更新答案的时间不是 \(\mathcal O(1)\)(一个数对答案的贡献与区间中别的数有关,例如比一个数小的数有多少,即 P5501)
const int MAXN=1e5;
const int MAXV=1<<14;
int n,qu,k,a[MAXN+5],blk_sz,blk_cnt;
int bel[MAXN+5],L[MAXN+5],R[MAXN+5];
ll pre[MAXN+5],suf[MAXN+5],anss[MAXN+5];
int buc[MAXV+5];
struct query{
int l,r,id;ll ans;
bool operator <(const query &rhs) const{
if(bel[l]^bel[rhs.l]) return bel[l]<bel[rhs.l];
else if(bel[l]&1) return r<rhs.r;
else return r>rhs.r;
}
} q[MAXN+5];
struct qwq{int l,r,id,op;};
vector<qwq> vl[MAXN+5],vr[MAXN+5];
int main(){
scanf("%d%d%d",&n,&qu,&k);vector<int> v;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<MAXV;i++) if(__builtin_popcount(i)==k) v.pb(i);
blk_sz=(int)pow(n,0.5);blk_cnt=(n-1)/blk_sz+1;
for(int i=1;i<=blk_cnt;i++){
L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,n);
for(int j=L[i];j<=R[i];j++) bel[j]=i;
}
for(int i=1;i<=qu;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+qu+1);int cl=1,cr=0;
for(int i=1;i<=n;i++){
pre[i]=pre[i-1];
for(int j:v) pre[i]+=buc[j^a[i]];
buc[a[i]]++;
} memset(buc,0,sizeof(buc));
for(int i=n;i;i--){
suf[i]=suf[i+1];
for(int j:v) suf[i]+=buc[j^a[i]];
buc[a[i]]++;
}
for(int i=1;i<=qu;i++){
q[i].ans=pre[q[i].r]-pre[cr]+suf[q[i].l]-suf[cl];
if(cl<q[i].l) vr[cr+1].pb({cl,q[i].l-1,i,1});
if(cl>q[i].l) vr[cr+1].pb({q[i].l,cl-1,i,-1});
if(cr<q[i].r) vl[q[i].l-1].pb({cr+1,q[i].r,i,-1});
if(cr>q[i].r) vl[q[i].l-1].pb({q[i].r+1,cr,i,1});
cl=q[i].l;cr=q[i].r;
} memset(buc,0,sizeof(buc));
for(int i=1;i<=n;i++){
for(int j:v) buc[a[i]^j]++;
for(int j=0;j<vl[i].size();j++){
int l=vl[i][j].l,r=vl[i][j].r,id=vl[i][j].id;
ll sum=0;for(int t=l;t<=r;t++) sum+=buc[a[t]];
q[id].ans+=sum*vl[i][j].op;
}
} memset(buc,0,sizeof(buc));
for(int i=n;i;i--){
for(int j:v) buc[a[i]^j]++;
for(int j=0;j<vr[i].size();j++){
int l=vr[i][j].l,r=vr[i][j].r,id=vr[i][j].id;
ll sum=0;for(int t=l;t<=r;t++) sum+=buc[a[t]];
q[id].ans+=sum*vr[i][j].op;
}
}
for(int i=2;i<=qu;i++) q[i].ans+=q[i-1].ans;
for(int i=1;i<=qu;i++) anss[q[i].id]=q[i].ans;
for(int i=1;i<=qu;i++) printf("%lld\n",anss[i]);
return 0;
}
/*
6 1 0
1 2 3 1 2 3
1 6
6 5 1
1 2 3 4 5 6
1 5
1 6
2 5
2 6
3 6
14 5 1
1 2 3 4 5 6 7 1 2 3 4 5 6 7
1 14
2 13
3 9
1 12
4 11
*/

浙公网安备 33010602011771号