SDOI 2009 HH的项链

题目描述 Description

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入描述 Input Description

第一行:一个整数N,表示项链的长度。

第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。

第三行:一个整数M,表示HH询问的个数。

接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出描述 Output Description

M行,每行一个整数,依次表示询问对应的答案。

样例输入 Sample Input

6

1 2 3 4 3 5

3

1 2

3 5

2 6

样例输出 Sample Output

2

2

4

数据范围及提示 Data Size & Hint

对于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 }

 

posted @ 2015-04-09 10:39  Xstsow  阅读(273)  评论(0编辑  收藏  举报