[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\n",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\n",ans[i]);
70 }

排序+树状数组

时间: 2024-10-05 23:57:11

[SDOI 2009]HH的项链的相关文章

BZOJ 1878 SDOI 2009 HH的项链 树状数组 + 离线处理

题目大意:有一些珠子串成的项链,珠子有不同的颜色.多次询问一段区间内有多少不同的颜色. 思路:这个题让我学会了一种巧妙的离线做法.将问题按左端点排序.处理出来每个颜色第一个出现的位置,和每个颜色下一个出现的位置.然后1到cnt循环,如果这里有一个问题的左端点是当前节点,就处理他的答案,方法是前缀合,可以用树状数组.然后把这个颜色的下一个出现的位置+1. 这样做就避免了一种颜色在询问中被处理两次. CODE: #include <cstdio> #include <cstring>

BZOJ 1878 SDOI 2009 HH项链 树状数组 + 脱机处理

标题效果:一些珠子项链.珠具有不同的颜色.我们问了很多次有多少种不同的颜色有过一段范围. 思考:这个问题让我学会聪明的离线实践.按左端点排序问题.加工出来的位置每种颜色首次出现.每一种颜色的下一次出现的位置.然后,1至cnt周期,这里有一个问题的左端点是当前节点,就处理他的答案.方法是前缀合,能够用树状数组.然后把这个颜色的下一个出现的位置+1. 这样做就避免了一种颜色在询问中被处理两次. CODE: #include <cstdio> #include <cstring> #in

[BZOJ 1875] [SDOI 2009] HH去散步【矩阵乘法】

题目链接:BZOJ - 1875 题目分析: 这道题如果去掉“不会立刻沿着刚刚走来的路走回”的限制,直接用邻接矩阵跑矩阵乘法就可以了.然而现在加了这个限制,建图的方式就要做一些改变.如果我们把每一条边看做点建矩阵,那么每次从一条边出发都只会到其他的边,不能仍然在这条边上“停留”,所以这就可以满足题目的限制.将每条边拆成两条单向边,比如一条编号为 4,一条编号为 5.那么 4^1=5, 5^1=4.这样只要不从第 i 条边走到 i 或 i^1 就可以了.初始的矩阵中以 A 为起点的边到达的方案数为

BZOJ 1875 SDOI 2009 HH去散步 矩阵乘法优化DP

题目大意:给出一张无向图,求从A到B走k步(不能走回头路)的方案数.(k <= 2^30) 思路:看到k的范围就知道是矩阵乘法了.关键是不能走回头路怎么构造.正常的方法构造点的转移不能避免这个问题,就用边来构造.只要保证不经过自己^1的边就可以保证不走回头路了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX

[SDOI 2009] HH去散步

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1875 [算法] 用f[i][j]表示现在在走了i步 , 在第j条边的方案数 矩阵加速 , 即可 时间复杂度 : O(N ^ 3logN) [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 125 const int P = 45989; struct edge { int to , nxt; } e[MA

P1972 [SDOI2009]HH的项链

P1972 [SDOI2009]HH的项链 2017-09-18 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越来越长.有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答--因为项链实在是太长了.于是,他只好求助睿智的你,来解决这个问题. 输入输出格式 输入格式: 第一行:一个整数N,表示项链的长度. 第二行:N 个整

BZOJ 1878: [SDOI2009]HH的项链( BIT )

离线处理 , 记下询问的左右端点并排序 , 然后可以利用树状数组 , 保证查询区间时每种颜色只计算一次 ------------------------------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define

luogu P1972 [SDOI2009]HH的项链

二次联通门 : luogu P1972 [SDOI2009]HH的项链 /* luogu P1972 [SDOI2009]HH的项链 莫队水过 记录一个count数组 来记录每个数出现了几次 缩小区间时只要看是否只出现一次 扩张区间时只要看看是否出现过即可 */ #include <algorithm> #include <cstdio> #include <cmath> #define Max 1000001 void read (int &now) { no

[bzoj1878] [SDOI2009]HH的项链(树状数组+离线)

1878: [SDOI2009]HH的项链 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 3210  Solved: 1619[Submit][Status][Discuss] Description HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此, 他的项链变得越来越长.有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的