正题
题目链接:https://www.luogu.com.cn/problem/P4867
题目大意
一个序列要求支持询问一个区间[l,r][l,r][l,r]内在[a,b][a,b][a,b]之间有多少种不同的权值。
解题思路
首先我们需要莫队,考虑用什么数据结构,如果我们使用线段树,那么复杂度将是O(mnlogn)O(m\sqrt n\log n)O(mnlogn)的,但是我们发现我们的修改次数虽然多,但是询问最多只有mmm次,那么我们考虑提升询问的时间复杂度降低修改的复杂度。
改为用分块来,这样询问时是O(n)O(\sqrt n)O(n)的但是修改时就是O(1)O(1)O(1)的,所以时间复杂度为O(nn)O(n\sqrt n)O(nn)的
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct node{int l,r,a,b,id;
}q[N*10];
int n,m,T,a[N],val[N],pos[N],ans[N*10];
int L[500],R[500],Val[500];
bool cmp(node x,node y){if(x.l/T==y.l/T)return x.r<y.r;return x.l<y.l;
}
void Add(int x)
{val[x]++;Val[pos[x]]+=(val[x]==1);}
void Del(int x)
{val[x]--;Val[pos[x]]-=(val[x]==0);}
int Query(int l,int r){int x=pos[l],y=pos[r],ans=0;if(x==y){for(int i=l;i<=r;i++)ans+=(val[i]>0);return ans;}for(int i=x+1;i<y;i++)ans+=Val[i];for(int i=l;i<=R[x];i++)ans+=(val[i]>0);for(int i=L[y];i<=r;i++)ans+=(val[i]>0);return ans;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&a[i]);T=sqrt(n);for(int i=1;i<=T;i++)L[i]=R[i-1]+1,R[i]=i*T;if(n!=T*T)L[++T]=R[T-1]+1,R[T]=n;for(int i=1;i<=T;i++)for(int j=L[i];j<=R[i];j++)pos[j]=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+1+m,cmp);int l=1,r=0;for(int i=1;i<=m;i++){while(r<q[i].r)Add(a[++r]);while(l>q[i].l)Add(a[--l]);while(r>q[i].r)Del(a[r--]);while(l<q[i].l)Del(a[l++]);ans[q[i].id]=Query(q[i].a,q[i].b);}for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}