NC22596 Rinne Loves Data Structure

题目链接

题目

题目描述

Rinne 喜欢 OI。在 9102 年的 PION 中,她在初赛遇到了这样一道题目:

阅读下列代码,然后回答问题。
img

补充:建树过程中会更新lc和rc,这实质上是一个二叉查找树的插入过程。

定义一个玄学节点叫做 R,每次操作读入 val ,执行 Insert(R,val)。

问题:每次 Insert 操作结束之后,输出当前节点的深度和。
这里我们定义 R 节点的深度为 0。

输入描述

第一行一个整数 N,表示操作次数。

接下来 N 行,第 i 行有一个值 \(val_i\),表示第 i 次操作的 \(val_i\)

输出描述

N 行,每行输出该次操作完后的答案。

示例1

输入

8
3
5
1
6
8
7
2
4

输出

0
1
2
4
7
11
13
15

备注

\(N \leq 3 \times 10^5\)

题解

知识点:树,STL。

画图易知二叉排序树插入的节点有 \(4\) 种情况:插入节点比所有节点都小,那会存放在最小节点的左孩子;插入节点比所有节点都大,那会存放在最大节点的右孩子;插入节点比某子树根小,但比其左子树根大,那会存放在左子树的右孩子;插入节点比某子树根大,但比其右子树根小,那会存放在右子树的左孩子。

因为二叉排序树不能出现相同键值的节点,用 \(set\) 存储比较好,特判一下插入相同元素的情况。再用一个 \(map\) 存储相应节点的深度。

随后用 \(set\) 成员函数 \(lower\_bound\) (比STL泛用函数要快)查找第一个大于等于插入元素的节点位置,然后如果是 \(begin()\) 或者 \(end()\) ,则取相应的头/尾节点的深度加一;如果是中间某个元素,则取前后两个元素的深度最大值加一。最后别忘记插入回去。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;



int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    unordered_map<int, int> dep;
    set<int> s;
    ll ans = 0;
    while (n--) {
        int val;
        cin >> val;
        if (s.empty()) {
            s.insert(val);
            dep[val] = 0;
            cout << 0 << '\n';
            continue;
        }
        auto pos = s.lower_bound(val);
        if (*pos == val) {
            cout << ans << '\n';
            continue;
        }
        if (pos == s.begin()) dep[val] = dep[*pos] + 1;
        else if (pos == s.end()) pos--, dep[val] = dep[*pos] + 1;
        else {
            dep[val] = dep[*pos];
            pos--;
            dep[val] = max(dep[*pos], dep[val]) + 1;
        }
        ans += dep[val];
        s.insert(val);
        cout << ans << '\n';
    }
    return 0;
}
posted @ 2022-07-09 16:22  空白菌  阅读(38)  评论(0编辑  收藏  举报