[BZOJ2906]

- 预处理出锅zbl
- 预处理f[i][j][k],g[i][j][k]分别表示第i块到第j块 k的出现次数和小于等于k的数的出现次数的平方和
- 查询的时候加上块外数的贡献即可
- 小trick:计算出每块的起始位置和终止位置
- 计算分块的大小时不要用除法,否则会RE
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
using namespace std;
const int N=5e4+10;
int blo,st[N],ed[N],cnt[20010],a[N],bl[N],f[41][41][20010];
ll g[41][41][20001],l,r,c,d,n,m,q;
ll work(int l,int r,int c,int d){
ll ans=0;
if(bl[l]==bl[r]||bl[l]==bl[r]-1){
rep(i,l,r){
if(a[i]>=c&&a[i]<=d){
cnt[a[i]]++;
}
}
rep(i,l,r){
if(a[i]>=c&&a[i]<=d){
ans+=1LL*cnt[a[i]]*cnt[a[i]];
cnt[a[i]]=0;
}
}
}else{
rep(i,l,blo*bl[l]) if(a[i]>=c&&a[i]<=d) cnt[a[i]]++;
rep(i,(bl[r]-1)*blo+1,r) if(a[i]>=c&&a[i]<=d) cnt[a[i]]++;
rep(i,l,blo*bl[l]){
if(a[i]>=c&&a[i]<=d){
int num=f[bl[l]+1][bl[r]-1][a[i]];
ans+=1LL*2*num*cnt[a[i]]+1LL*cnt[a[i]]*cnt[a[i]];
cnt[a[i]]=0;
}
}
rep(i,(bl[r]-1)*blo+1,r){
if(a[i]>=c&&a[i]<=d){
int num=f[bl[l]+1][bl[r]-1][a[i]];
ans+=1LL*2*num*cnt[a[i]]+1LL*cnt[a[i]]*cnt[a[i]];
cnt[a[i]]=0;
}
}
ans+=g[bl[l]+1][bl[r]-1][d]-g[bl[l]+1][bl[r]-1][c-1];
}
return ans;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&q); blo=pow(n,0.6666666);
rep(i,1,n)scanf("%d",&a[i]);
rep(i,1,n){
bl[i]=(i-1)/blo+1;
if(!st[bl[i]])st[bl[i]]=i;
ed[bl[i]]=i;
}
rep(i,1,bl[n])
rep(j,i,bl[n])
rep(k,st[i],ed[j])
f[i][j][a[k]]++;//就是这里写挂了
rep(i,1,bl[n])
rep(j,i,bl[n])
rep(k,1,m)
g[i][j][k]=g[i][j][k-1]+(ll)f[i][j][k]*f[i][j][k];//就是这里写挂了
ll ans=0;
while(q--){
scanf("%lld%lld%lld%lld",&l,&r,&c,&d);
l^=ans,r^=ans,c^=ans,d^=ans;
ans=work(l,r,c,d);
printf("%lld\n",ans);
}return 0;
}