[SDOI 2009]HH的项链

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。

题解

这题首先在线是没法做的,所以我们可以考虑离线算法

解法1:莫队裸题,分块就好。

 1 #include<map>
 2 #include<queue>
 3 #include<stack>
 4 #include<cmath>
 5 #include<ctime>
 6 #include<cstdio>
 7 #include<string>
 8 #include<vector>
 9 #include<cstdlib>
10 #include<cstring>
11 #include<iostream>
12 #include<algorithm>
13 #define LL long long
14 #define RE register
15 #define IL inline
16 using namespace std;
17 const int M=1000000;
18 const int N=50000;
19 const int Q=200000;
20 
21 int cnt[M+5],ans;
22 int keep[Q+5];
23 int n,m,a[N+5],tim;
24 struct query
25 {
26     int l,r,id;
27 }q[Q+5];
28 bool comp(const query &a,const query &b){return a.l/tim==b.l/tim ? a.r<b.r:a.l<b.l;}
29 
30 int main()
31 {
32     scanf("%d",&n);
33     tim=sqrt(n);
34     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
35     scanf("%d",&m);
36     for (int i=1;i<=m;i++)
37     {
38         scanf("%d%d",&q[i].l,&q[i].r);
39         q[i].id=i;
40     }
41     sort(q+1,q+m+1,comp);
42     int curl=0,curr=0,l,r;
43     for (int i=1;i<=m;i++)
44     {
45         l=q[i].l,r=q[i].r;
46         while (curl<l) {cnt[a[curl]]--;ans-=(cnt[a[curl++]]==0);}
47         while (curl>l) {cnt[a[--curl]]++;ans+=(cnt[a[curl]]==1);}
48         while (curr<r) {cnt[a[++curr]]++;ans+=(cnt[a[curr]]==1);}
49         while (curr>r) {cnt[a[curr]]--;ans-=(cnt[a[curr--]]==0);}
50         keep[q[i].id]=ans;
51     }
52     for (int i=1;i<=m;i++) printf("%d
",keep[i]);
53     return 0;
54 }
莫队

解法2:排序+树状数组。

首先记录下每种颜色的下一种颜色所在的位置,将所有询问按照右端点进行排序。

左往右扫,扫过一个元素将该位置++,若有前驱,将前驱--。

扫到区间端点,答案为$sum[r]-sum[l-1]$,记录一下就好。(其中$sum$为前缀和)

前缀和用树状数组实现。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 struct Node
 7 {
 8     int l,r,num;
 9 }s[200001];
10 int a[50001],ans[200001],n,m,c[100001],vis[1000001];
11 bool cmp(Node a,Node b)
12 {
13     return (a.r<b.r||(a.r==b.r&&a.l<b.l));
14 }
15 int getsum(int x)
16 {
17     int s=0;
18     while (x)
19     {
20         s+=c[x];
21         x-=(x&(-x));
22     }
23     return s;
24 }
25 void add(int x,int d)
26 {
27     while (x<=n)
28     {
29         c[x]+=d;
30         x+=(x&(-x));
31     }
32 }
33 int main()
34 {int i,j;
35     cin>>n;
36     for (i=1;i<=n;i++)
37     {
38         scanf("%d",&a[i]);
39     }
40     cin>>m;
41     for (i=1;i<=m;i++)
42     {
43         scanf("%d%d",&s[i].l,&s[i].r);
44         s[i].num=i;
45     }
46     sort(s+1,s+m+1,cmp);
47     j=1;
48     for (i=1;i<=n+1;i++)
49     {
50         while (j<=m&&i>s[j].r)
51         {
52             ans[s[j].num]=getsum(s[j].r)-getsum(s[j].l-1);
53             j++;
54         }
55         if (i>n) break;
56         if (vis[a[i]])
57         {
58             add(vis[a[i]],-1);
59             vis[a[i]]=i;
60             add(i,1);
61         }
62         else 
63         {
64             vis[a[i]]=i;
65             add(i,1);
66         }
67     }
68     for (i=1;i<=m;i++)
69     printf("%d
",ans[i]);
70 }
排序+树状数组
原文地址:https://www.cnblogs.com/NaVi-Awson/p/7260245.html