题目背景
无
题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
第一行:一个整数N,表示项链的长度。
第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。
第三行:一个整数M,表示HH 询问的个数。
接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
输出格式
M 行,每行一个整数,依次表示询问对应的答案。
输入输出样例
输入 #1复制
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出 #1复制
2
2
4
说明/提示
对于20%的数据,n,m≤5000n,m\leq 5000n,m≤5000
对于40%的数据,n,m≤105n,m\leq 10^5n,m≤105
对于60%的数据,n,m≤5×105n,m\leq 5\times 10^5n,m≤5×105
对于所有数据,n,m≤1×106n,m\leq 1\times 10^6n,m≤1×106
本题可能需要较快的读入方式,最大数据点读入数据约20MB
题目大意 : 有一个长为 N 的序列,M次询问, 每次询问一个区间有多少个不同的数
思路 : 在线 + 主席树,离线 + 树状数组,莫队,这三种方法都可以写, 这里只介绍树状数组的(最简单的就是这个了)
首先需要了解一个东西, 查询区间内某数出现的次数, 该数一定只放在最后一次出现的位置, 就拿样例来说, N = 7, 序列为1, 3, 4, 5, 7, 1, 9。 1这个数在第一个位置和第6个位置都有出现, 但是对于一个右端点大于6的区间, 我们只算第6个位置的次数(想想就明白了), 基于这样一种性质, 每次一个数出现后, 如果是第一次出现, 就将该位置的数的次数 + 1, 否则将原位置的数次数 - 1, 该位置 + 1, 但是这样一来就只能一直往右更新了, 否则你后面再查询到左边的区间, 那个你已经更新过了,所以需要对询问进行排序, 按照右区间从小到大, 再结合原序列, 依次处理就好
Accepted code
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e6 + 100;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }
struct node
{
int l, r, ori;
}t[MAXN];
bool cmp(node a, node b) {
return a.r < b.r;
}
int p[MAXN], c[MAXN], n, m;
int res[MAXN], vis[MAXN]; // res为离线后的答案, vis为该数上一次的出现位置
void add(int x, int y) {
while (x <= n) {
c[x] += y;
x += lowbit(x);
}
}
int Query(int x) {
int ans = 0;
while (x) {
ans += c[x];
x -= lowbit(x);
}
return ans;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) sc("%d", &p[i]);
cin >> m;
for (int i = 0; i < m; i++) {
sc("%d %d", &t[i].l, &t[i].r);
t[i].ori = i;
}
sort(t, t + m, cmp);
int tmp = 1; // 更新到了哪个位置
for (int i = 0; i < m; i++) {
int L = t[i].l, R = t[i].r;
for (int j = tmp; j <= R; j++) { // 从上一次位置开始更新
if (vis[p[j]]) add(vis[p[j]], -1); // 出现过就删去原位置
vis[p[j]] = j; add(j, 1); // 新位置加上
}
int scnt = Query(R) - Query(L - 1);
res[t[i].ori] = scnt; // 保存答案
tmp = R + 1;
}
for (int i = 0; i < m; i++) printf("%d\n", res[i]);
return 0;
}