bzoj1878[SDOI2009]HH的项链

bzoj1878[SDOI2009]HH的项链

题意:

N个数,M个询问求区间[L,R]中包含了多少种不同的数。

题解:

莫队好像可以做~但正解是树状数组。先将询问按左端点排序,并求出每个数的下一个与它相等的数的位置,同时将每个数第一次出现的位置在树状数组中置为1,此时query(x)求出来的就是1到x里有多少个不同的数。枚举排序后的询问,将当前左端点向右移动,每右移一位就将原位置的数的下一个与它相同的数的位置在树状数组中置为1,保证如果这个数在[l,r]中出现不会漏算。当左端点移动到询问的左端点位置时,就输出query(r)-query(l-1),表示l到r里有多少个不同的数。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define inc(i,j,k) for(int i=j;i<=k;i++)
 5 #define maxn 50100
 6 #define lb(x) x&-x;
 7 using namespace std;
 8 
 9 int a[maxn],last[maxn*20],next[maxn],sm[maxn],n,m;
10 struct ask{int l,r,ans,id;}; ask asks[maxn*6];
11 bool cmp1(ask a,ask b){return a.l<b.l;}
12 bool cmp2(ask a,ask b){return a.id<b.id;}
13 inline void update(int x,int v){while(x<=n){sm[x]+=v,x+=lb(x);}}
14 inline int query(int x){int q=0; while(x>0){q+=sm[x],x-=lb(x);} return q;}
15 int main(){
16     scanf("%d",&n); inc(i,1,n)scanf("%d",&a[i]);
17     scanf("%d",&m); inc(i,1,m)scanf("%d%d",&asks[i].l,&asks[i].r),asks[i].id=i;
18     memset(last,0,sizeof(last)); inc(i,1,n){if(last[a[i]])next[last[a[i]]]=i;else update(i,1); last[a[i]]=i;}
19     sort(asks+1,asks+1+m,cmp1); int l=1;
20     inc(i,1,m){
21         while(l<asks[i].l){if(next[l])update(next[l],1); l++;}
22         asks[i].ans=query(asks[i].r)-query(asks[i].l-1);
23     }
24     sort(asks+1,asks+1+m,cmp2); inc(i,1,m)printf("%d\n",asks[i].ans);
25     return 0;
26 }

 

20160525

posted @ 2016-07-23 13:09  YuanZiming  阅读(132)  评论(0编辑  收藏  举报