P4887 第十四分块(前体)
链接
题解
二次离线莫队。。新技能get。。
用于可以使用莫队且单次转移的效率不是 (O(1)) 的情况。
对于每次指针移动,先不直接算,而是把询问差分然后再次离线询问。
这样可以算出每一问对于上一个询问的答案的变化量,最后再重新按末对的顺序加起来就可以。
(Code)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,K,B;
int a[N],be[N];
struct query{
int l,r,id;
}q[N];
bool cmp(query x,query y){
if(be[x.l]!=be[y.l]) return be[x.l]<be[y.l];
return x.r<y.r;
}
//pre[x][0]=f(x,[1,x-1]) pre[x][1]=f(x,[1,x])
LL pre[N][2],s[N];
LL ans[N];
vector<int> c;
struct P{
int l,r,id,f;
P(int lx=0,int rx=0,int idx=0,int fx=0){
l=lx;r=rx;id=idx;f=fx;
}
};
vector<P> ve[N];
int main(){
scanf("%d%d%d",&n,&m,&K);B=sqrt(n);
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
be[i]=(i+B-1)/B;
}
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;
}
sort(q+1,q+1+m,cmp);
for(int i=0;i<16384;++i)
if(__builtin_popcount(i)==K) c.push_back(i);
for(int i=1;i<=n;++i){
for(int j=0;j<c.size();++j) ++s[a[i]^c[j]];
pre[i+1][0]=s[a[i+1]];pre[i][1]=s[a[i]];
}
memset(s,0,sizeof(s));
int l=1,r=0;
for(int i=1;i<=m;++i){
if(r<q[i].r) ve[l-1].push_back(P(r+1,q[i].r,q[i].id,-1));
while(r<q[i].r){++r;ans[q[i].id]+=pre[r][0];}
//r++ f(x,[l,x-1])=f(x,[1,x-1])-f(x,[1,l-1])
if(l>q[i].l) ve[r].push_back(P(q[i].l,l-1,q[i].id,1));
while(l>q[i].l){--l;ans[q[i].id]-=pre[l][1];}
//l-- f(x,[x+1,r])=f(x,[1,r])-f(x,[1,x])
if(r>q[i].r) ve[l-1].push_back(P(q[i].r+1,r,q[i].id,1));
while(r>q[i].r){ans[q[i].id]-=pre[r][0];--r;}
//r-- -f(x,[l,x-1])=-f(x,[1,x-1])+f(x,[1,l-1])
if(l<q[i].l) ve[r].push_back(P(l,q[i].l-1,q[i].id,-1));
while(l<q[i].l){ans[q[i].id]+=pre[l][1];++l;}
//l++ -f(x,[x+1,r])=-f(x,[1,r])+f(x,[1,x])
}
for(int i=1;i<=n;++i){
for(int j=0;j<c.size();++j) ++s[a[i]^c[j]];
for(int j=0;j<ve[i].size();++j){
for(int k=ve[i][j].l;k<=ve[i][j].r;++k){
ans[ve[i][j].id]+=(LL)ve[i][j].f*s[a[k]];
}
}
}
for(int i=2;i<=m;++i){
ans[q[i].id]+=ans[q[i-1].id];
}
for(int i=1;i<=m;++i) {
print(ans[i]);
puts("");
}
return 0;
}