洛谷 P1972 [SDOI2009]HH的项链(树状数组,离线)
传送门
解题思路
因为是求区间的不同种类数,所以我们用树状数组(貌似并没有什么直接联系)
(...表示到)
还是和原来一样,用s[i]来表示a[i-lowbit(i)]...a[i]的种类数。
因为有一个类似于去重的操作,所以就有一个数组记录一下is[i]表示编号为i的贝壳上一次出现的地方,每一次更新结果时s[is[i]]--;s[i]++;is[i]=i。
但是我们想,假设询问的一个区间为a...b,下一个区间为a...b-5,并且x这种贝壳在b-3,b-7都出现过(a<b-7<b-5<b-3<b),那么我们在询问a...b时使s[b-7]--;然后再询问a...b-5时就会导致答案出错(少算了一个)。(解释的不太清楚,感性理解一下)
所以这里我们用到一个操作——离线,所谓离线,就是一次性把问题全部输入,根据一定的顺序排序(便于解题),保存好答案后再根据原顺序输出。
怎样用程序实现离线呢?我们可以用结构体来存储数据,每一个结构体变量中有一个保存数值,另一个保存序号,全部读入后按照数值的关键字sort一遍,输出答案前按照关键字序号再sort一遍,这样就达到了目的。
比如说这一道题:
1 struct ques { 2 int l,r,ans,id; 3 } q[maxn]; 4 bool cmp1(ques a,ques b) { 5 return a.r<b.r; 6 } 7 bool cmp2(ques a,ques b) { 8 return a.id<b.id; 9 } 10 int main() { 11 //读入数据 12 sort(q+1,q+m+1,cmp1);//第一遍sort 13 //求出答案 14 sort(q+1,q+m+1,cmp2);//第二遍sort 15 //输出答案 16 return 0; 17 }
AC代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 using namespace std; 5 const int maxn=500005; 6 int s[maxn],n,a[maxn],m; 7 int is[maxn*2]; 8 inline int lowbit(int x) { 9 return x&(-x); 10 } 11 void insert(int id,int k) { 12 for(int i=id; i<=n; i+=lowbit(i)) { 13 s[i]+=k; 14 } 15 } 16 int query(int id) { 17 int res=0; 18 for(int i=id; i>0; i-=lowbit(i)) { 19 res+=s[i]; 20 } 21 return res; 22 } 23 struct ques { 24 int l,r,ans,id; 25 } q[maxn]; 26 bool cmp1(ques a,ques b) { 27 return a.r<b.r; 28 } 29 bool cmp2(ques a,ques b) { 30 return a.id<b.id; 31 } 32 int main() { 33 cin>>n; 34 int cnt=1; 35 for(int i=1; i<=n; i++) { 36 scanf("%d",&a[i]); 37 } 38 cin>>m; 39 for(int i=1; i<=m; i++) { 40 scanf("%d%d",&q[i].l,&q[i].r); 41 q[i].id=i; 42 } 43 sort(q+1,q+m+1,cmp1); 44 for(int i=1; i<=n; i++) { 45 if(is[a[i]]) { 46 insert(is[a[i]],-1); 47 } 48 insert(i,1); 49 is[a[i]]=i; 50 while(q[cnt].r==i&&cnt<=m) { 51 q[cnt].ans=query(q[cnt].r)-query(q[cnt].l-1); 52 cnt++; 53 } 54 } 55 sort(q+1,q+m+1,cmp2); 56 for(int i=1; i<=m; i++) { 57 printf("%d\n",q[i].ans); 58 } 59 return 0; 60 }