题解 P4887 【模板】莫队二次离线(第十四分块(前体))
题面传送门
莫队二次离线 mol ban tea,大概是这道题让我第一次听说有这东西?
首先看到这类数数对的问题可以考虑莫队,记 如果你过了我请你吃糖
考虑优化,我们记
上文所叙述的都是右端点移动的情况,左端点移动的情况大体上也差不多,还是设左端点由
最后,由于我们求出的是每次莫队后答案的增量,还需对每次询问的结果求一遍前缀和即可得到每次询问真正的答案。
时间复杂度
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
*/