对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
Day2
没有修改的操作,可以进行离线处理。
按照左端点排序了之后,将所有第一次出现的数字都记为1,其余记为0。同时要另存出每一个数下一个与它相同的数的出现的位置。
头指针首先指向开头,之后后移。如果头指针遇到了某一个区间的左端点(排序后)就查询这个区间右端点的前缀和,即为当前区间的种类数。需要注意的是,一旦头指针移动过后,相应的点如果为1的话应该清零,然后将下一个和它数字相同的位置置1,这样就保证了之前的点对后面的点没有影响。
算法的正确性是显而易见的。求前缀和的话树状数组更方便,线段树也可以。
【代码】
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define N 50005 #define maxn 200005 #define MAXN 1000005 using namespace std; struct hp{ int l,r,num; }que[maxn]; int next[N],head[MAXN],a[N],A[N],c[N],Ans[maxn]; bool b[MAXN]; int n,m,ANS,j; inline int in(){ int x=0; char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } int cmp(hp a,hp b){ return a.l<b.l; } void add(int loc,int value){ for (int i=loc;i<=n;i+=i&(-i)) c[i]+=value; } int query(int loc){ int ans=0; for (int i=loc;i>0;i-=i&(-i)) ans+=c[i]; return ans; } int main(){ n=in(); for (int i=1;i<=n;++i) a[i]=in(); m=in(); for (int i=1;i<=m;++i) que[i].l=in(),que[i].r=in(),que[i].num=i; sort(que+1,que+m+1,cmp); for (int i=n;i>=1;--i){ if (head[a[i]]!=-1) next[i]=head[a[i]]; head[a[i]]=i; } for (int i=1;i<=n;++i) if (!b[a[i]]){ b[a[i]]=true; A[i]=1; } for (int i=1;i<=n;++i) add(i,A[i]); int j=1; while (j<=m&&que[j].l==1){ ANS=query(que[j].r); Ans[que[j].num]=ANS; j++; } for (int i=2;i<=n;++i){ add(i-1,-1); if (next[i-1]) add(next[i-1],1); while (j<=m&&que[j].l==i){ ANS=query(que[j].r); Ans[que[j].num]=ANS; j++; } } for (int i=1;i<=m;++i) printf("%d\n",Ans[i]); }