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

[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;
}
  • 36
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于洛谷上的p1036题目,我们可以使用Python来解决。下面是一个可能的解法: ```python def dfs(nums, target, selected_nums, index, k, sum): if k == 0 and sum == target: return 1 if index >= len(nums) or k <= 0 or sum > target: return 0 count = 0 for i in range(index, len(nums)): count += dfs(nums, target, selected_nums + [nums[i]], i + 1, k - 1, sum + nums[i]) return count if __name__ == "__main__": n, k = map(int, input().split()) nums = list(map(int, input().split())) target = int(input()) print(dfs(nums, target, [], 0, k, 0)) ``` 在这个解法中,我们使用了深度优先搜索(DFS)来找到满足要求的数列。通过递归的方式,我们遍历了所有可能的数字组合,并统计满足条件的个数。 首先,我们从给定的n和k分别表示数字个数和需要选取的数字个数。然后,我们输入n个数字,并将它们存储在一个列表nums中。接下来,我们输入目标值target。 在dfs函数中,我们通过迭代index来选择数字,并更新选取的数字个数k和当前总和sum。如果k等于0且sum等于target,我们就找到了一个满足条件的组合,返回1。如果index超出了列表长度或者k小于等于0或者sum大于target,说明当前组合不满足要求,返回0。 在循环中,我们不断递归调用dfs函数,将选取的数字添加到selected_nums中,并将index和k更新为下一轮递归所需的值。最终,我们返回所有满足条件的组合个数。 最后,我们在主程序中读入输入,并调用dfs函数,并输出结果。 这是一种可能的解法,但不一定是最优解。你可以根据题目要求和测试数据进行调试和优化。希望能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值