SDOI 2009 HH的项链
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
M行,每行一个整数,依次表示询问对应的答案。
6
1 2 3 4 3 5
3
1 2
3 5
2 6
2
2
4
对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
真心是蒟蒻,看答案都看了半天。最后写的时候还忘记了数据被我排序处理过。
首先,这个题目要用离线算法,也就是把所有数据都读入进来后,在进行运算。我们将需要查找的区间按右端点升序排列起来(左端点顺序无所谓)。
接下来,我们用树状数组来解决剩下的问题。
我们从第一颗珠子开始,逐个扫描,如果该类珠子是第一次出现,那么我们就记录下他的位置;如果他出现之前还有同类珠子出现,那我们就把他前面的那一个珠子去掉,然后记录下这颗珠子的位置。在树状数组中,每扫描到一个珠子,都要进行一次ADD(1)的操作。去掉珠子就需要找到要去的珠子的位置,然后在线段树上执行ADD(-1)的操作。
在扫描珠子的同时,我们不断的判断离散化后的区间,如果当前判断的区间的右端点恰为我们扫描到的珠子,那么他的答案也就是 SUM(R)-SUM(L-1)。
务必要保证扫描珠子和判断区间同时进行。先扫描珠子,那么在判断区间时,SUM(R)和SUM(L-1),有可能被执行过ADD(-1)操作,也就是说求得的数据可能小于正确答案。
代码如下
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 int l[200002], r[200002], n, m, a[50001], pre[1000001], c[50001], z[200002], ans[200002]; 6 7 8 void qsort(int s, int t) { //排序 9 int i = s, j = t, x = r[(s+t)/2]; 10 while (i < j) { 11 while (r[i] < x) i++; 12 while (r[j] > x) j--; 13 if (i <= j) { 14 int k = r[i]; 15 r[i] = r[j]; 16 r[j] = k; 17 k = l[i]; 18 l[i] = l[j]; 19 l[j] = k; 20 k = z[i]; 21 z[i] = z[j]; 22 z[j] = k; 23 i++; 24 j--; 25 } 26 } 27 if (s < j) qsort(s, j); 28 if (i < t) qsort(i, t); 29 } 30 31 int lowbit(int x) { //lowbit函数 32 return x&-x; 33 } 34 35 int sum(int x) { //求前缀和 36 int sum1 = 0; 37 while (x > 0) { 38 sum1 += c[x]; x -= lowbit(x); 39 } 40 return sum1; 41 } 42 43 void add(int x, int k) { //改值 44 while (x <= n) { 45 c[x] += k; x += lowbit(x); 46 } 47 } 48 49 int main() { 50 ios::sync_with_stdio(false); 51 scanf("%d", &n); 52 for (int i = 1; i <= n; i++) 53 scanf("%d", &a[i]); 54 scanf("%d", &m); 55 for (int i = 1; i <= m; i++) { 56 scanf("%d%d", &l[i], &r[i]); 57 z[i] = i; 58 } 59 qsort(1, m); 60 int p = 1, q = 1; //p为当前区间,q为当前珠子 61 while (p <= m) { 62 int tail = r[p]; 63 while (q <= tail) { 64 add(q, 1); 65 if (pre[a[q]] != 0) //被搜索过 66 add(pre[a[q]], -1); 67 pre[a[q]] = q; 68 q++; 69 } 70 while (r[p] == tail) { 71 ans[z[p]] = sum(r[p]) - sum(l[p] - 1); //把答案放入原先对应位置 72 p++; 73 } 74 } 75 for (int i = 1; i <= m; i++) 76 cout << ans[i] << "\n"; 77 return 0; 78 }