luogu P1972 [SDOI2009]HH的项链 树状数组
Posted iat14
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P1972 [SDOI2009]HH的项链 树状数组相关的知识,希望对你有一定的参考价值。
之前只做过分块做法,补一下树状数组做法。
我们先考虑一个问题,如何求从[1,x]这一区间内元素不同的个数?显然我们只要从到到位,遇到一个新的元素,就在对应位置+1,然后使用树状数组求前缀和即可。
这里我们需要去求[x,y],所求区间的左端点也会发生变化。我们先按照[1,x]的方法预处理出这个前缀和数组。我们考虑对询问区间按照左端点排序。然后对于当前的区间[x0,y0],query(y0) - query(x0 - 1)与正确答案相比有所区别,是因为有些元素在x0左侧计算过了,而在这段区间中,对应第一次出现的位置没有+1。我们考虑能不能弥补这个错误呢。我们预处理出nxt[i],表示与位置i元素相同的右侧最近元素的下标。这样子,比如上一个区间为[x1,y1],下一个区间为[x2,y2],那么我们就让[x1,x2)这段区间中的每一个i的nxt[i]位置都对应+1。如果nxt[i]在[x1,x2)内,则不会对以后的答案造成影响,如果nxt[i]在[x2,]后,则显然修正了这个错误,并且在[i,x2)这段区间中,不会再有相同的元素了。
忘记了树状数组的lowbit x & (-x)必须有括号。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 struct qry 6 { 7 int l,r,id; 8 friend bool operator < (qry a,qry b) 9 { 10 return a.l < b.l; 11 } 12 } vec[510000]; 13 int n,m; 14 int num[510000],nxt[510000],hav[1100000],res[510000],sum[510000]; 15 int lowbit(int x) 16 { 17 return x & (-x); 18 } 19 void tre_insert(int x,int val) 20 { 21 for (int i = x;i <= n;i += lowbit(i)) 22 sum[i] += val; 23 } 24 int tre_qry(int x) 25 { 26 int tot = 0; 27 for (int i = x;i;i -= lowbit(i)) 28 tot += sum[i]; 29 return tot; 30 } 31 int main() 32 { 33 scanf("%d",&n); 34 for (int i = 1;i <= n;i++) 35 scanf("%d",&num[i]); 36 scanf("%d",&m); 37 for (int i = 1;i <= m;i++) 38 { 39 scanf("%d%d",&vec[i].l,&vec[i].r); 40 vec[i].id = i; 41 } 42 sort(vec + 1,vec + m + 1); 43 for (int i = 1;i <= n;i++) 44 if (hav[num[i]] == 0) 45 { 46 hav[num[i]] = 1; 47 tre_insert(i,1); 48 } 49 memset(hav,0,sizeof(hav)); 50 for (int i = n;i >= 1;i--) 51 { 52 if (hav[num[i]] == 0) 53 nxt[i] = n + 1; 54 else 55 nxt[i] = hav[num[i]]; 56 hav[num[i]] = i; 57 } 58 int t = 1; 59 for (int i = 1;i <= m;i++) 60 { 61 for (;t <= vec[i].l - 1;t++) 62 tre_insert(nxt[t],1); 63 res[vec[i].id] = tre_qry(vec[i].r) - tre_qry(vec[i].l - 1); 64 } 65 for (int i = 1;i <= m;i++) 66 printf("%d ",res[i]); 67 return 0; 68 }
以上是关于luogu P1972 [SDOI2009]HH的项链 树状数组的主要内容,如果未能解决你的问题,请参考以下文章