传送门
莫队+树状数组的思路很显然,但是时间 O(mn√log2n)
莫队+权值分块的做法比较优越。将权值分块之后修改可以做到 O(1) 查询 O(n√)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int max_n=1e5+5;
const int max_m=1e6+5;
const int max_sqrt=350;
int n,m,t,num,ans;
int a[max_n],val[max_n],sum[max_sqrt];
struct hp{int l,r,a,b,id,final;}q[max_m];
inline int cmp(hp a,hp b){
int num1,num2;
if (a.l%t==0) num1=a.l/t; else num1=a.l/t+1;
if (b.l%t==0) num2=b.l/t; else num2=b.l/t+1;
return num1<num2||(num1==num2&&a.r<b.r);
}
inline int ask(int l,int r){
int ans=0,k,lrange,rrange,numl,numr;
if (l%t==0) numl=l/t;
else numl=l/t+1;
if (r%t==0) numr=r/t;
else numr=r/t+1;
if (numl==numr){
for (int i=l;i<=r;++i)
if (val[i]) ans++;
return ans;
}
lrange=numl+1;
k=numl*t;
for (int i=l;i<=k;++i)
if (val[i]) ans++;
k=(numr-1)*t+1;
for (int i=k;i<=r;++i)
if (val[i]) ans++;
rrange=numr-1;
for (int i=lrange;i<=rrange;++i)
ans+=sum[i];
return ans;
}
int main(){
scanf("%d%d",&n,&m);
t=(int)sqrt(n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
for (int i=1;i<=m;++i) scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b),q[i].id=i;
sort(q+1,q+m+1,cmp);
for (int i=q[1].l;i<=q[1].r;++i){
if (a[i]%t==0) num=a[i]/t; else num=a[i]/t+1;
++val[a[i]];
if (val[a[i]]==1) ++sum[num];
}
ans=ask(q[1].a,q[1].b);
q[q[1].id].final=ans;
for (int i=2;i<=m;++i){
if (q[i-1].l<q[i].l)
for (int j=q[i-1].l;j<q[i].l;++j){
if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
--val[a[j]];
if (!val[a[j]]) --sum[num];
}
if (q[i-1].l>q[i].l)
for (int j=q[i-1].l-1;j>=q[i].l;--j){
if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
++val[a[j]];
if (val[a[j]]==1) ++sum[num];
}
if (q[i-1].r<q[i].r)
for (int j=q[i-1].r+1;j<=q[i].r;++j){
if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
++val[a[j]];
if (val[a[j]]==1) ++sum[num];
}
if (q[i-1].r>q[i].r)
for (int j=q[i-1].r;j>q[i].r;--j){
if (a[j]%t==0) num=a[j]/t; else num=a[j]/t+1;
--val[a[j]];
if (!val[a[j]]) --sum[num];
}
ans=ask(q[i].a,q[i].b);
q[q[i].id].final=ans;
}
for (int i=1;i<=m;++i)
printf("%d\n",q[i].final);
}