BZOJ1878: [SDOI2009]HH的项链

1878: [SDOI2009]HH的项链

Time Limit: 4 Sec  Memory Limit: 64 MB
Submit: 1673  Solved: 808
[Submit][Status]

Description

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

Input

第一行:一个整数N,表示项链的长度。 第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 第三行:一个整数M,表示HH询问的个数。 接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

Output

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

Sample Input

6
1 2 3 4 3 5
3
1 2
3 5
2 6

Sample Output

2
2
4

HINT


对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。

Source

题解:
这题以前做树状数组的时候做过,只不过现在早把思想忘了,今天再来做一下。
巧妙的运用了前缀和,使得树状数组发挥作用。
这题树状数组有两种写法,具体见代码:
1.按右端点排序
 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<iostream>
 7 #include<vector>
 8 #include<map>
 9 #include<set>
10 #include<queue>
11 #define inf 1000000000
12 #define maxn 50000+1000
13 #define maxm 200000+10000
14 #define maxk 1000000+1000
15 #define eps 1e-10
16 #define ll long long
17 #define pa pair<int,int>
18 using namespace std;
19 inline int read()
20 {
21     int x=0,f=1;char ch=getchar();
22     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
23     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
24     return x*f;
25 }
26 struct rec{int x,y,id;}a[maxm];
27 int s[maxn],ans[maxm],next[maxn],head[maxk],n,m;
28 void add(int x,int y)
29 {
30     for(;x<=n;x+=x&(-x))s[x]+=y;
31 }
32 int sum(int x)
33 {
34     int t=0;
35     for(;x;x-=x&(-x))t+=s[x];
36     return t;
37 }
38 bool cmp(rec a,rec b)
39 {
40     return a.y<b.y;
41 }
42 int main()
43 {
44     freopen("input.txt","r",stdin);
45     freopen("output.txt","w",stdout);
46     n=read();
47     for(int i=1;i<=n;i++)
48      {
49          int x=read();
50          next[i]=head[x];
51          head[x]=i;
52      }
53     m=read();
54     for(int i=1;i<=m;i++)
55      a[i].x=read(),a[i].y=read(),a[i].id=i;
56     sort(a+1,a+m+1,cmp);
57     int now=0;
58     for(int i=1;i<=m;i++)
59     {
60         while(now<a[i].y)
61         {
62             now++;
63             add(next[now]+1,1);
64             add(now+1,-1);
65         }
66         ans[a[i].id]=sum(a[i].x);
67     }  
68     for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
69     return 0;
70 }
View Code

2.按左端点排序

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<iostream>
 7 #include<vector>
 8 #include<map>
 9 #include<set>
10 #include<queue>
11 #define inf 1000000000
12 #define maxn 50000+1000
13 #define maxm 200000+10000
14 #define maxk 1000000+1000
15 #define eps 1e-10
16 #define ll long long
17 #define pa pair<int,int>
18 using namespace std;
19 inline int read()
20 {
21     int x=0,f=1;char ch=getchar();
22     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
23     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
24     return x*f;
25 }
26 struct rec{int x,y,id;}a[maxm];
27 int s[maxn],ans[maxm],next[maxn],head[maxk],n,m,mx,b[maxn];
28 void add(int x,int y)
29 {
30     for(;x<=n;x+=x&(-x))s[x]+=y;
31 }
32 int sum(int x)
33 {
34     int t=0;
35     for(;x;x-=x&(-x))t+=s[x];
36     return t;
37 }
38 bool cmp(rec a,rec b)
39 {
40     return a.x==b.x?a.y<b.y:a.x<b.x;
41 }
42 int main()
43 {
44     freopen("input.txt","r",stdin);
45     freopen("output.txt","w",stdout);
46     n=read();
47     for(int i=1;i<=n;i++)b[i]=read();
48     for(int i=n;i>=1;i--)
49      {
50          next[i]=head[b[i]];
51          head[b[i]]=i;
52          mx=max(mx,b[i]);
53      }
54     m=read();
55     for(int i=1;i<=m;i++)
56      a[i].x=read(),a[i].y=read(),a[i].id=i;
57     sort(a+1,a+m+1,cmp);
58     for(int i=1;i<=mx;i++)if(head[i])add(head[i],1);
59     int now=1;
60     for(int i=1;i<=m;i++)
61     {
62         while(now<a[i].x)
63         {
64             if(next[now])add(next[now],1); 
65             now++;
66         }
67         ans[a[i].id]=sum(a[i].y)-sum(a[i].x-1);
68     }  
69     for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
70     return 0;
71 }
View Code

 

本题数据规模较大,需要合理使用数据结构。题目中有两个元素:区间和颜色。如果直接使用线段树套平衡树,需要牵扯到合并操作,时间复杂度很高(比如当询问类似[2,N-1]的时候)。因此,需要做一些预处理工作使得询问更容易解决。

观察在某一区间内出现的某种颜色,假设有若干个同色点都在这个区间中,而该颜色只能计算一遍,因此我们需要找出一个有代表性的点,当然是第一个最有代表性。观察该点,以及它前后同色点的位置,有什么可以利用的规律?很显然,它的上一个同色点肯定在区间左侧,而同区间内的该颜色的其他点,它们的上一个同色点显然在区间内。这样,我们的工作便是找到询问区间[l,r]中这样的点x的个数:点x的上一个同色点在询问区间[l,r]左侧。

到这一步,我们有一个在线算法:建线段树,将询问分成O(lbN)个小区间分别处理。如果再套平衡树,需要二分查找才能求符合条件的点的个数。这样总的理论最坏复杂度为O(M(lbN)3),虽然实际情况会好一些,但还是难以符合要求。我们分析一下该算法复杂度高的原因:平衡树难以很好支持求个数的运算,需要二分来实现。那么求个数效率最高的是什么数据结构?当然是树状数组。

当然,线段树是无法再套树状数组的,那样空间复杂度会达到O(N2),所以我们要舍弃线段树,寻找新的算法。对于一堆无序的询问区间,如果没有线段树,便很难处理。因此我们考虑将区间按左端点排序,使其有序,然后从左到右扫描每个点。再考虑那些上一个同色点在询问区间左侧的点,我们的目的是快速求出该区间中这种点的个数。而这些点的上一个同色点因为在询问区间左侧,所以已经被扫描过,而区间内其他点却没有!这样,如果我们换个角度,改上一个下一个,预处理出每个点i的下一个同色点的位置Next[i],并且在扫描过该点i后使树状数组C[Next[i]]=1。那么对于询问区间[l,r],答案便是Sumc[r]-Sumc[l-1]!这样,算法只有1”求和两种操作。问题到此得到圆满解决, 最终时间复杂度为O(MlbN)

posted @ 2014-08-25 18:31  ZYF-ZYF  Views(621)  Comments(0Edit  收藏  举报