核心问题:快速求出各个区间的不同数的个数,数的值域为:[1,1e6] (即可用O(n)数组存下)。
在线算法难以快速维护,尝试先对询问处理,采用离线算法。将询问按区间右端点r升序排列,当r确定时,一个数是否对这个询问做过贡献,只需看它在r左面的最右数是否在这个区间内,即用右端点左面的最右数来等效代替了重复且难以处理的数。设一个布尔数组f[n],表示i位置是否为当前r时某个数的最右数。可用树状数组维护其前缀和(好像nlogn的复杂度只能用树状数组,因为题目有点卡常)。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 5 using namespace std; 6 7 const int N=1e6+5; 8 9 int a[N],n,m,lst[N],tre[N]; 10 11 struct node{ 12 int l,r,ord,ans; 13 }q[N]; 14 15 inline int read() 16 { 17 int x=0; 18 char ch=getchar(); 19 while(!isdigit(ch)) 20 ch=getchar(); 21 while(isdigit(ch)) 22 x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 23 return x; 24 } 25 26 inline int cmp1(const node &u,const node &v) 27 { 28 return u.r<v.r; 29 } 30 31 inline int cmp2(const node &u,const node &v) 32 { 33 return u.ord<v.ord; 34 } 35 36 inline int lowbit(const int &x) 37 { 38 return x&(-x); 39 } 40 41 inline void insert(int k,int x) 42 { 43 for(;k<=n;k+=lowbit(k)) 44 tre[k]+=x; 45 } 46 47 inline int fin(int k) 48 { 49 int ret=0; 50 for(;k>=1;k-=lowbit(k)) 51 ret+=tre[k]; 52 return ret; 53 } 54 55 int main() 56 { 57 n=read(); 58 for(int i=1;i<=n;++i) 59 a[i]=read(); 60 m=read(); 61 for(int i=1;i<=m;++i) 62 q[i].l=read(),q[i].r=read(),q[i].ord=i; 63 sort(q+1,q+m+1,cmp1); 64 int k=0; 65 for(int cnt=1;cnt<=m;++cnt) 66 { 67 while(k<q[cnt].r) 68 { 69 ++k; 70 if(lst[a[k]]) 71 insert(lst[a[k]],-1); 72 insert(k,1); 73 lst[a[k]]=k; 74 } 75 q[cnt].ans=fin(q[cnt].r)-fin(q[cnt].l-1); 76 } 77 sort(q+1,q+m+1,cmp2); 78 for(int i=1;i<=m;++i) 79 printf("%d ",q[i].ans); 80 return 0; 81 }