洛谷 P1972 [SDOI2009] HH的项链 题解 主席树

发布于:2024-04-17 ⋅ 阅读:(140) ⋅ 点赞:(0)

[SDOI2009] HH的项链

题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。

有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入格式

一行一个正整数 n n n,表示项链长度。
第二行 n n n 个正整数 a i a_i ai,表示项链中第 i i i 个贝壳的种类。

第三行一个整数 m m m,表示 HH 询问的个数。
接下来 m m m 行,每行两个整数 l , r l,r l,r,表示询问的区间。

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

2
2
4

提示

【数据范围】

对于 20 % 20\% 20% 的数据, 1 ≤ n , m ≤ 5000 1\le n,m\leq 5000 1n,m5000
对于 40 % 40\% 40% 的数据, 1 ≤ n , m ≤ 1 0 5 1\le n,m\leq 10^5 1n,m105
对于 60 % 60\% 60% 的数据, 1 ≤ n , m ≤ 5 × 1 0 5 1\le n,m\leq 5\times 10^5 1n,m5×105
对于 100 % 100\% 100% 的数据, 1 ≤ n , m , a i ≤ 1 0 6 1\le n,m,a_i \leq 10^6 1n,m,ai106 1 ≤ l ≤ r ≤ n 1\le l \le r \le n 1lrn

本题可能需要较快的读入方式,最大数据点读入数据约 20MB

原题

洛谷P1972——传送门

代码

#include <bits/stdc++.h>
using namespace std;
#define max_Heap(x) priority_queue<x, vector<x>, less<x>>
#define min_Heap(x) priority_queue<x, vector<x>, greater<x>>
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<long long, long long> PLL;
const double PI = acos(-1);

const int maxn = 1e6 + 6;
// 主席树需记录左孩子,右孩子及节点值
struct node
{
    int lc, rc, val;
} tree[maxn << 5]; // 开树节点空间

int root[maxn]; // 每一棵线段树的根节点
int idx;        // 当前节点序号

int build(int st, int ed, int root, int x, int f)
{
    int now_idx = ++idx;        // 创建新节点
    tree[now_idx] = tree[root]; // 复制根节点
    tree[now_idx].val += f;     // f为1则加1,f为-1则减1
    if (st == ed)
        return now_idx;
    int mid = (st + ed) / 2; // 二分建树
    if (x <= mid)
        tree[now_idx].lc = build(st, mid, tree[root].lc, x, f);
    else
        tree[now_idx].rc = build(mid + 1, ed, tree[root].rc, x, f);
    return now_idx;
}

int query(int l, int r, int st, int ed, int root2, int root1)
{
    if (l <= st && r >= ed)
        return tree[root2].val - tree[root1].val; // 第r棵树减去第l-1棵树即为l到r区间的个数
    int mid = (st + ed) / 2;
    int ans = 0;
    if (l <= mid)
        ans += query(l, r, st, mid, tree[root2].lc, tree[root1].lc);
    if (r > mid)
        ans += query(l, r, mid + 1, ed, tree[root2].rc, tree[root1].rc);
    return ans;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    unordered_map<int, int> mp; // 存储某值上一次出现的位置,没出现时pos为0
    int n, m;
    cin >> n;
    int tmp;                     // 存储读入数据
    for (int i = 1; i <= n; i++) // 建第i棵线段树
    {
        cin >> tmp;
        root[i] = build(0, n + 6, root[i - 1], mp[tmp], 1);
        mp[tmp] = i; // 更新tmp的值前一次出现的位置
    }
    cin >> m;
    int l, r;
    while (m--)
    {
        cin >> l >> r;
        cout << query(0, l - 1, 0, n + 6, root[r], root[l - 1]) << '\n';
    }

    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到