luogu P4887 【模板】莫队二次离线(第十四分块(前体))
题面传送门
考虑一般的莫队。复杂度\(O(n\sqrt nC_{14}^{7})\)
这里面转移的复杂度略微有点大。
考虑\([l,r]\)转移到\([l,r+1]\)的贡献。即\(a_{r+1}\)对于\([l,r]\)的贡献。
这个东西可以差分,变成\([1,r]-[1,l-1]\),然后就可以统一计算了。
剩下四种情况同上,注意\(k=0\)的情况。
这就是大名鼎鼎的莫队二次离线,可以将\(O(n\sqrt nk)\)变成\(O(nk+n\sqrt n)\)
代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define R register
#define CI const int &
using namespace std;
int n,m,k,x,y,z,a[100039],ks,q[100039],f[32039],tots;
int w[100039],head;
long long ans[100039],tot[100039];
struct ques{int l,r,id;}s[100039];
struct yyy{int x,y,id,flag;};
vector<yyy> g[100039];
inline bool cmp(ques x,ques y){return (x.l/ks==y.l/ks)?(((x.l/ks)&1)?x.r>y.r:x.r<y.r):(x.l<y.l);}
inline void make(CI x=0,CI y=0,CI z=0){
if(x==14){if(y==k)w[++head]=z;return;}
if(y<k) make(x+1,y+1,z^(1<<x));
make(x+1,y,z);
}
inline void get(R int x){
R int i;
for(i=1;i<=head;i++) f[x^w[i]]++;
}
inline void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
R int i,j,h,lon,l=1,r=0;R ques tmps;R yyy tmp;
read(n);read(m);read(k);ks=sqrt(n);make();
if(k>14){for(i=1;i<=m;i++) printf("0\n");return 0;}
for(i=1;i<=n;i++)read(a[i]);
for(i=1;i<=n;i++) q[i]=f[a[i]],get(a[i]);
for(i=1;i<=m;i++) read(x),read(y),s[i]=(ques){x,y,i};
sort(s+1,s+m+1,cmp);
for(i=1;i<=m;i++){
tmps=s[i];
if(r<tmps.r){
g[l-1].push_back((yyy){r+1,tmps.r,i,-1});
while(r<tmps.r) ans[i]+=q[++r];
}
if(r>tmps.r){
g[l-1].push_back((yyy){tmps.r+1,r,i,1});
while(r>tmps.r) ans[i]-=q[r--];
}
if(l>tmps.l){
g[r].push_back((yyy){tmps.l,l-1,i,1});
while(l>tmps.l) ans[i]-=q[--l];
}
if(l<tmps.l){
g[r].push_back((yyy){l,tmps.l-1,i,-1});
while(l<tmps.l) ans[i]+=q[l++];
}
}
for(memset(f,0,sizeof(f)),i=1;i<=n;i++){
get(a[i]);lon=g[i].size();
for(j=0;j<lon;j++){
tmp=g[i][j];
for(h=tmp.x;h<=tmp.y;h++){
if(h<=i&&!k)ans[tmp.id]+=tmp.flag*(f[a[h]]-1);
else ans[tmp.id]+=tmp.flag*f[a[h]];
}
}
}
for(i=1;i<=m;i++) ans[i]+=ans[i-1],tot[s[i].id]=ans[i];
for(i=1;i<=m;i++) printf("%lld\n",tot[i]);
}