基本上每个网站上都有,就不粘提面啦。
这道题要求维护区间种类数,不支持区间加减法。
所以需要通过一些转换使其满足树状数组/线段树的条件。
首先,对于段区间中的数,若有数重复出现,则只需关心最后出现的一个。
所以,我们可以对所有查询区间的r非降排序,从左向右推now_r并同时维护一个树状数组。
树状数组表示一段区间数的种类数,具体看一个栗子:
1,2,1,3
当now_r=1,insert(1,1)表示在位置1出现了一个未重复的数,需要在树状数组上对应的位置+1,此时树状数组对应序列每个位置上的值为:1 0 0 0
当now_r=2,insert(2,1):1 1 0 0
当now_r=1,由于数a[3](就是1)之前出现过,最后一遍出现的地方为1,则insert(1,-1),insert(3,1),即减去之前位置加上的种类数转而在右边的位置+1,:0 1 1 0
当now_r=3,insert(4,1):0 1 1 1
这样,每次i更新时看是否i=一个r,while输出sum[l,r]=sum[r]-sum[l-1](询问[2,3]=sum[3]-sum[2-1]=2)
最后一点,为了记录每个数最后一遍出现过的位置,需要一个last数组。last[i]=0表示数i还未出现过,last[i]=k表示数i最后一次出现的位置为k。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1000001,N=50001,M=200001; 6 int last[maxn],sum[N],a[N],ans[M],n; 7 struct fx{ 8 int l,r,num; 9 }q[M]; 10 bool cmp(fx a,fx b){ 11 return a.r<b.r; 12 } 13 int query(int),lowbit(int); 14 void insert(int,int); 15 int main(){ 16 int m,nowques,tmp; 17 memset(last,0,sizeof(last)); 18 scanf("%d",&n); 19 for (int i=1;i<=n;i++) 20 scanf("%d",&a[i]); 21 scanf("%d",&m); 22 for (int i=1;i<=m;i++){ 23 scanf("%d %d",&q[i].l,&q[i].r); 24 q[i].num=i; 25 } 26 sort(q+1,q+m+1,cmp); 27 nowques=1; 28 for (int i=1;i<=n;i++){ 29 insert(i,1); 30 if (last[a[i]]) insert(last[a[i]],-1); 31 last[a[i]]=i; 32 tmp=query(i); 33 while (q[nowques].r==i&&nowques<=m){ 34 ans[q[nowques].num]=tmp-query(q[nowques].l-1); 35 nowques++; 36 } 37 } 38 for (int i=1;i<=m;i++) 39 printf("%d\n",ans[i]); 40 } 41 int query(int x){ 42 int s=0; 43 for (int i=x;i;i-=lowbit(i)) 44 s+=sum[i]; 45 return s; 46 } 47 void insert(int x,int v){ 48 for (int i=x;i<=n;i+=lowbit(i)) 49 sum[i]+=v; 50 } 51 int lowbit(int x){ 52 return x&(-x); 53 }