BZOJ1878: [SDOI2009]HH的项链
【传送门:BZOJ1878】
简要题意:
给出一个长度为n的数列,给出m个询问,每个询问输入l,r,输出数列中l到r的不同的数的个数
题解:
看这数据范围就不能用在线来做,那就只能离线了
很显然就是树状数组
首先将询问按照l从小到大的排序排一遍
设一个数组next,next[i]表示下一个与第i个数相同的数的位置
我们从左到右扫一遍数列,如果扫到x这个位置,将x这个位置--,就next[x]这个位置++,然后就通过求前缀和的方法来得出每个询问的答案就可以了
参考代码:
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int k[51000]; struct question { int l,r,d,id; }q[210000]; bool cmp(question n1,question n2){return n1.l<n2.l;} bool cmpi(question n1,question n2){return n1.id<n2.id;} int a[51000]; int lowbit(int x){return x&-x;} int getsum(int x) { int ans=0; while(x!=0) { ans+=a[x]; x-=lowbit(x); } return ans; } int n; void change(int x,int c) { while(x<=n) { a[x]+=c; x+=lowbit(x); } } int c[1100000]; int next[51000]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&k[i]); int m; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+m+1,cmp); memset(next,0,sizeof(next)); memset(c,0,sizeof(c)); for(int i=n;i>=1;i--) { next[i]=c[k[i]]; c[k[i]]=i; } memset(a,0,sizeof(a)); for(int i=1;i<=1000000;i++) if(c[i]!=0) change(c[i],1); int l=1; for(int i=1;i<=m;i++) { while(l<q[i].l) { change(l,-1); if(next[l]!=0) change(next[l],1); l++; } q[i].d=getsum(q[i].r)-getsum(q[i].l-1); } sort(q+1,q+m+1,cmpi); for(int i=1;i<=m;i++) printf("%d\n",q[i].d); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚