【数据结构】可持久化线段树(主席树)

文章目录

  • 接下来是一道例题
  • 再放一道==标记永久化+主席树==
  • 再加一道==主席树+在线处理==

主席树 即为 可持久化线段树,是一种可以记录每一个修改版本的数据结构。

难以进行区间的修改操作

主席树存储的信息

struct Node
{int l, r; // 左结点和右结点int cnt; // 区间内有多少数
};

下面以图示表示主席树记录修改的过程

在这里插入图片描述

接下来是一道例题

第k小数

给定长度为 N N N 的整数序列 A A A,下标为 1 ∼ N 1∼N 1N

现在要执行 M M M 次操作,其中第 i i i 次操作为给出三个整数 l i , r i , k i l_i,r_i,k_i li,ri,ki,求 A [ l i ] , A [ l i + 1 ] , … , A [ r i ] A[l_i],A[l_i+1],…,A[r_i] A[li],A[li+1],,A[ri] (即 A A A 的下标区间 [ l i , r i ] [l_i,r_i] [li,ri]中第 k i k_i ki 小的数是多少。

输入格式

第一行包含两个整数 N N N M M M

第二行包含 N N N 个整数,表示整数序列 A A A

接下来 M M M 行,每行包含三个整数 l i , r i , k i l_i,r_i,k_i li,ri,ki,用以描述第 i i i 次操作。

输出格式

对于每次操作输出一个结果,表示在该次操作中,第 k k k 小的数的数值。

每个结果占一行。

数据范围

N ≤ 105 , M ≤ 104 , ∣ A [ i ] ∣ ≤ 109 N≤105,M≤104,|A[i]|≤109 N105,M104,A[i]109

输入样例:

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

输出样例:

5
6
3

思路

参考前缀和的想法,找到 1 − r 1\ -\ r 1  r 1 − l − 1 1\ -\ l-1 1  l1 的区间元素个数,然后二分求解

代码

#include <bits/stdc++.h>using namespace std;const int N = 100010, M = 10010;int n, m;
int a[N]; // 原数组
vector<int> nums; // 离散化数组struct Node
{int l, r;int cnt;
}tr[N * 4 + N * 17];int root[N], idx; // 存以每个元素结尾的数组的线段树int find(int x) // 离散化 找x对应的序号
{return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}int build(int l, int r)
{int p = ++ idx; // 新结点编号if (l == r) return p; // 说明是叶子结点int mid = l + r >> 1;tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r); // 建树return p;
}// 在p结点的基础上
int insert(int p, int l, int r, int x)
{int q = ++ idx; // 新结点编号tr[q] = tr[p]; // 先复制一下原来的结点(也就是i-1版) 在原来结点的基础上进行修改if (l == r)    // 叶子结点返回{tr[q].cnt ++ ;return q;}// 在刚刚复制原来的结点时已经记录了l和r的信息 现在看哪一边需要修改就修改哪一边int mid = l + r >> 1;if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);else tr[q].r = insert(tr[p].r, mid + 1, r, x);tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt; // 更新最新的cnt 可以理解为线段树里的pushup操作return q; // 返回结点编号
}int query(int q, int p, int l, int r, int k)
{if (l == r) return r; // 叶子结点直接返回int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;int mid = l + r >> 1;// k小于cnt说明要找的点在左半边,否则在右半边if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i ++ ){cin >> a[i];nums.push_back(a[i]);}// 离散化去重sort(nums.begin(), nums.end());nums.erase(unique(nums.begin(), nums.end()), nums.end());root[0] = build(0, nums.size() - 1); // 构造初版线段树for (int i = 1; i <= n; i ++ )root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i])); // 在i-1的版本上修改形成第i版while (m -- ){int l, r, k;cin >> l >> r >> k;cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)] << '\n';}
}

再放一道标记永久化+主席树

TTM - To the moon

一个长度为 N N N 的数组 { A } \{A\} {A} 4 4 4 种操作 :

  • C l r d:区间 [ l , r ] [l,r] [l,r] 中的数都加 d d d ,同时当前的时间戳加 1 1 1

  • Q l r:查询当前时间戳区间 [ l , r ] [l,r] [l,r] 中所有数的和 。

  • H l r t:查询时间戳 t t t 区间 [ l , r ] [l,r] [l,r] 的和 。

  • B t:将当前时间戳置为 t t t

所有操作均合法 。

ps:刚开始时时间戳为 0 0 0

输入格式,一行 N N N M M M,接下来 M M M 行每行一个操作

输出格式:对每个查询输出一行表示答案

数据保证: 1 ≤ N , M ≤ 1 0 5 1\le N,M\le 10^5 1N,M105 ∣ A i ∣ ≤ 1 0 9 |A_i|\le 10^9 Ai109 1 ≤ l ≤ r ≤ N 1\le l \le r \le N 1lrN ∣ d ∣ ≤ 1 0 4 |d|\le10^4 d104。在刚开始没有进行操作的情况下时间戳为 0 0 0,且保证 B 操作不会访问到未来的时间戳。`

输入格式

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出格式

4
55
9
15

code

#include <bits/stdc++.h>using namespace std;#define int long longconst int N = 100010;int n, m;
int a[N];struct Node
{int l, r; // 左右结点编号int ll, rr; // 左右边界int sum; // 区间元素和int lazy;
}tr[N * 40];int root[N], idx;int build(int l, int r)
{int p = ++ idx;tr[p].ll = l, tr[p].rr = r;if (l == r){tr[p].sum = a[l];return p;}int mid = l + r >> 1;tr[p].l = build(l, mid);tr[p].r = build(mid + 1, r);tr[p].sum = tr[tr[p].l].sum + tr[tr[p].r].sum + tr[p].lazy * (r - l + 1);return p;
}// l-r是要修改的部分 p是要修改的结点
int insert(int p, int l, int r, int x)
{int q = ++ idx;tr[q] = tr[p];if (tr[q].ll >= l && tr[q].rr <= r){tr[q].lazy += x;tr[q].sum += (tr[q].rr - tr[q].ll + 1) * x;return q;}int mid = tr[q].ll + tr[q].rr >> 1;if (l <= mid) tr[q].l = insert(tr[p].l, l, r, x);if (r > mid) tr[q].r = insert(tr[p].r, l, r, x);tr[q].sum = tr[tr[q].l].sum + tr[tr[q].r].sum + tr[q].lazy * (tr[q].rr - tr[q].ll + 1);return q;
}int query(int p, int l, int r, int lazy)
{if (tr[p].ll >= l && tr[p].rr <= r) return tr[p].sum + lazy * (tr[p].rr - tr[p].ll + 1);int mid = tr[p].ll + tr[p].rr >> 1;int res = 0;lazy += tr[p].lazy;if (l <= mid) res += query(tr[p].l, l, r, lazy);if (r > mid) res += query(tr[p].r, l, r, lazy);return res;
}signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i ++ ) cin >> a[i];root[0] = build(1, n);int now = 0;for (int i = 1; i <= m; i ++ ){char op;int l, r, d, t;cin >> op;if (op == 'C'){cin >> l >> r >> d;now ++ ;root[now] = insert(root[now - 1], l, r, d);}else if (op == 'Q'){cin >> l >> r;cout << query(root[now], l, r, tr[root[now]].lazy) << '\n';}else if (op == 'H'){cin >> l >> r >> t;cout << query(root[t], l, r, tr[root[t]].lazy) << '\n';}else{cin >> t;now = t;}}
}

再加一道主席树+在线处理

D. Jamie and To-do List

Why I have to finish so many assignments???

Jamie is getting very busy with his school life. He starts to forget the assignments that he has to do. He decided to write the things down on a to-do list. He assigns a value priority for each of his assignment (lower value means more important) so he can decide which he needs to spend more time on.

After a few days, Jamie finds out the list is too large that he can’t even manage the list by himself! As you are a good friend of Jamie, help him write a program to support the following operations on the to-do list:

  • set a**i x**i — Add assignment a**i to the to-do list if it is not present, and set its priority to x**i. If assignment a**i is already in the to-do list, its priority is changed to x**i.
  • remove a**i — Remove assignment a**i from the to-do list if it is present in it.
  • query a**i — Output the number of assignments that are more important (have a smaller priority value) than assignment a**i, so Jamie can decide a better schedule. Output - 1 if a**i is not in the to-do list.
  • undo d**i — Undo all changes that have been made in the previous d**i days (not including the day of this operation)

At day 0, the to-do list is empty. In each of the following q days, Jamie will do exactly one out of the four operations. If the operation is a query, you should output the result of the query before proceeding to the next day, or poor Jamie cannot make appropriate decisions.

Input

The first line consists of a single integer q ( 1 ≤ q ≤ 1 0 5 ) q (1 ≤ q ≤ 10^5) q(1 q 105) — the number of operations.

The following q lines consists of the description of the operations. The i-th line consists of the operation that Jamie has done in the i-th day. The query has the following format:

The first word in the line indicates the type of operation. It must be one of the following four: set, remove, query, undo.

  • If it is a set operation, a string a**i and an integer x i x_i xi follows ( 1 ≤ x i ≤ 109 ) (1 ≤ x_i ≤ 109) (1 xi 109). a**i is the assignment that need to be set to priority x i x_i xi.
  • If it is a remove operation, a string a i a_i ai follows. a i a_i ai is the assignment that need to be removed.
  • If it is a query operation, a string a i a_i ai follows. a i a_i ai is the assignment that needs to be queried.
  • If it is a undo operation, an integer d i d_i di follows ( 0 ≤ d i < i ) (0 ≤ d_i < i) (0 di<i). d i d_i di is the number of days that changes needed to be undone.

All assignment names a i a_i ai only consists of lowercase English letters and have a length 1 ≤ ∣ a i ∣ ≤ 15 1 ≤ |a_i| ≤ 15 1  ∣ai∣  15.

It is guaranteed that the last operation is a query operation.

Output

For each query operation, output a single integer — the number of assignments that have a priority lower than assignment a i a_i ai, or − 1 - 1  1 if a i a_i ai is not in the to-do list.

Interaction

If the operation is a query, you should output the result of the query and flush the output stream before proceeding to the next operation. Otherwise, you may get the verdict Idleness Limit Exceed.

For flushing the output stream, please refer to the documentation of your chosen programming language. The flush functions of some common programming languages are listed below:

  • C: fflush(stdout);
  • C++: cout « flush;
  • Java: System.out.flush();

Examples

input

8
set chemlabreport 1
set physicsexercise 2
set chinesemockexam 3
query physicsexercise
query chinesemockexam
remove physicsexercise
query physicsexercise
query chinesemockexam

output

1
2
-1
1

code

一棵线段树无法维护时可以同时维护两个线段树

#include <bits/stdc++.h>using namespace std;const int N = 11;
const int INF = 1e9 + 10;int n, tot;
map<int, int> nums; // 离散化
map<string, int> m;struct Operation
{string op;string test;int x;
};struct Node
{int l, r;int cnt;
}tr[N * 50];int root1[N], root2[N], idx;int insert(int p, int l, int r, int pos, int op, string s)
{int q = ++ idx;tr[q] = tr[p];if (l == r){tr[q].cnt += op;return q;}int mid = l + r >> 1;if (pos <= mid) tr[q].l = insert(tr[q].l, l, mid, pos, op, s);else tr[q].r = insert(tr[q].r, mid + 1, r, pos, op, s);if (tr[q].l == tr[q].r) tr[q].cnt = tr[tr[q].l].cnt;else tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;return q;
}int query(int q, int l, int r, int nl, int nr)
{if (r == -1) return 0;if (nl >= l && nr <= r) return tr[q].cnt;int mid = nl + nr >> 1;int res = 0;if (l <= mid) res += query(tr[q].l, l, r, nl, mid);if (r > mid) res += query(tr[q].r, l, r, mid + 1, nr);return res;
}int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> n;vector<Operation> ope(n + 1);for (int i = 1; i <= n; i ++ ){cin >> ope[i].op;string op = ope[i].op;if (op == "set"){cin >> ope[i].test >> ope[i].x;nums[ope[i].x] = tot;string s = ope[i].test;int x = ope[i].x;if (!query(root1[i - 1], 0, n - 1, tot, tot)){root1[i] = insert(root1[i - 1], 0, n - 1, tot, x, s);root2[i] = insert(root2[i - 1], 0, INF, x, 1, s);}else{root1[i] = insert(root1[i - 1], 0, n - 1, tot, x, s);root2[i] = insert(root2[i - 1], 0, INF, x, 1, s);root2[i] = insert(root2[i - 1], 0, INF, m[s], -1, s);}m[s] = x;tot ++ ;}else if (op == "remove"){cin >> ope[i].test;string s = ope[i].test;if (!query(root1[i - 1], 0, n - 1, tot, tot)){root1[i] = root1[i - 1];root2[i] = root2[i - 1];continue;}root1[i] = insert(root1[i - 1], 0, n - 1, nums[m[s]], m[s], s);root2[i] = insert(root2[i - 1], 0, INF, m[s], -1, s);}else if (op == "query"){cin >> ope[i].test;root1[i] = root1[i - 1];root2[i] = root2[i - 1];string s = ope[i].test;if (!query(root1[i - 1], 0, n - 1, tot, tot)){cout << -1 << '\n';cout << flush;}else{cout << query(root2[i], 0, m[s] - 1, 0, INF) << '\n';cout << flush;}}else{cin >> ope[i].x;int d = ope[i].x;root1[i] = root1[i - d - 1];root2[i] = root2[i - d - 1];}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635774.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1.php开发-个人博客项目文章功能显示数据库操作数据接收

&#xff08;2022-day12&#xff09; #知识点 1-php入门&#xff0c;语法&#xff0c;提交 2-mysql 3-HTMLcss ​ 演示案例 博客-文章阅读功能初步实现 实现功能&#xff1a; 前端文章导航&#xff0c;点入内容显示&#xff0c;更改ID显示不同内容 实现步骤&#xff1…

WebSocket协议、与HTTP对比

WebSocket 也可前往本人的个人网站进行阅读 WebSocket 和 HTTP WebSocket和HTTP协议一样&#xff0c;都是基于TCP协议实现的应用层协议。 HTTP协议通常是单边通信&#xff0c;主要用于传输静态文档、请求-响应通信&#xff0c;适用于Web浏览器加载网页、API调用等。然而Web…

编译和链接(翻译环境:预编译+编译+汇编+链接​、运行环境)

一、翻译环境和运行环境​ 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。​ 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。​ 第2种是执行环境&#xff0c;它用于实际执行代码。​ VS中编译器&#xff1a;cl.exe &#xff1b;Linux中…

INTEWORK—PET 汽车软件持续集成平台

产品概述 INTEWORK-PET-CI是经纬恒润自主研发的汽车软件持续集成&持续交付平台&#xff0c;在传统的持续集成基础上深化了研运一体化&#xff08;DevOps&#xff09;的概念&#xff0c;将嵌入式软件中的拉取代码、检查、构建、测试、版本管理以及发布交付等环节串联起来&am…

【微信小程序独立开发 4】基本信息编辑

这一节完成基本信息的编辑和保存 首先完成用户头像的获取 头像选择 需要将 button 组件 open-type 的值设置为 chooseAvatar&#xff0c;当用户选择需要使用的头像之后&#xff0c;可以通过 bindchooseavatar 事件回调获取到头像信息的临时路径。 从基础库2.24.4版本起&…

初阶数据结构:顺序表

目录 1. 引子&#xff1a;线性表2. 简单数据结构&#xff1a;顺序表2.1 顺序表简介与功能模块分析2.2 顺序表的实现2.2.1 顺序表&#xff1a;存储数据结构的构建2.2.2 顺序表&#xff1a;初始化与空间清理&#xff08;动态&#xff09;2.2.3 顺序表&#xff1a;插入与删除数据2…

爬虫笔记(一):实战登录古诗文网站

需求&#xff1a;登录古诗文网站&#xff0c;账号&#xff0b;密码&#xff0b;图形验证码 第一&#xff1a;自己注册一个账号&#xff0b;密码哈 第二&#xff1a;图形验证码&#xff0c;需要一个打码平台&#xff08;充钱&#xff0c;超能力power&#xff01;&#xff09;或…

信息登记小程序怎么做_重塑用户互动,开启全新营销篇章

信息登记小程序&#xff1a;重塑用户互动&#xff0c;开启全新营销篇章 在数字化浪潮中&#xff0c;小程序以其便捷、高效的特点&#xff0c;逐渐成为企业与用户之间沟通的桥梁。其中&#xff0c;信息登记小程序更是凭借其独特的定位&#xff0c;在众多小程序中脱颖而出。本文…

[C#]winform部署openvino调用padleocr模型

【官方框架地址】 https://github.com/PaddlePaddle/PaddleOCR 【算法介绍】 OpenVINO和PaddleOCR都是用于计算机视觉应用的工具&#xff0c;但它们有不同的特点和用途。OpenVINO是一个由Intel开发的开源工具套件&#xff0c;主要用于加速深度学习推理&#xff0c;而PaddleOC…

LNMP环境下综合部署动态网站

目录 LNMP部署--nginx 搭建mysql数据库 安装mysql的过程&#xff1a; 部署PHP&#xff1a; ​编辑​编辑php的配置文件在哪 wordpress程序安装 LNMP部署--nginx 纯净--联网状态 环境变量中没有nginx 安装形式的选择&#xff1a; yum安装&#xff1a;自动下载安装包及…

贪心算法 ——硬币兑换、区间调度、

硬币兑换&#xff1a; from book&#xff1a;挑战程序设计竞赛 思路&#xff1a;优先使用大面额兑换即可 package mainimport "fmt"func main() {results : []int{}//记录每一种数额的张数A : 620B : A//备份cnts : 0 //记录至少需要多少张nums : []int{1, 5, 10, 5…

大数据质量管制规范示例

大数据质量管制规范示例 一、前提概要二、相关概念三、管理原则四、治理委员五、应用管理六、查验方式七、考核比率八、扣分标准九、责任划分十、追责范围十一、其它条例十二、总结 一、前提概要 在当今大数据信息时代&#xff0c;大数据平台&#xff08;大数据平台开发规范示…

关于大模型学习中遇到的3

来源&#xff1a;网络 Embedding模型 随着大型语言模型的发展&#xff0c;以ChatGPT为首&#xff0c;涌现了诸如ChatPDF、BingGPT、NotionAI等多种多样的应用。公众大量地将目光聚焦于生成模型的进展之快&#xff0c;却少有关注支撑许多大型语言模型应用落地的必不可少的Embed…

中仕教育:社会人员可以报名三支一扶吗?

三支一扶是针对高校应届毕业生的一种考试&#xff0c;社会人员并不满足报考条件&#xff0c;所以不能报考。 三支一扶报考条件&#xff1a; 1.应届毕业生或者毕业两年内未就业的往届毕业生。 2.专科以上学历 3.遵纪守法&#xff0c;作风良好。 不同地区的考试要求不同&…

【AJAX框架】AJAX入门与axios的使用

文章目录 前言一、AJAX是干什么的&#xff1f;二、AJAX的安装2.1 CDN引入2.2 npm安装 三、基础使用3.1 CDN方式3.2 node方式 总结 前言 在现代Web开发中&#xff0c;异步JavaScript和XML&#xff08;AJAX&#xff09;已经成为不可或缺的技术之一。AJAX使得网页能够在不刷新整个…

【C++】std::string 转换成非const类型 char* 的三种方法记录

std::string 有两个方法&#xff1a;data() 和 c_str()&#xff0c;都是返回该字符串的const char类型&#xff0c;那如何转换成非const的char呢&#xff1f; 下面展示三种方法&#xff1a; 强转&#xff1a;char* char_test (char*)test.c_str();使用string的地址&#xff…

LeetCode 热题 100 | 双指针(下)

目录 42. 接雨水 1 方法一&#xff1a;我的方法 2 方法二&#xff1a;动态规划 3 方法三&#xff1a;双指针 菜鸟做题第一周&#xff0c;语言是 C 42. 接雨水 1 方法一&#xff1a;我的方法 Warning&#xff1a;这是我的智障做法&#xff0c;请勿模仿 我只能说它教会…

用Go plan9汇编实现斐波那契数列计算

斐波那契数列是一个满足递推关系的数列&#xff0c;如&#xff1a;1 1 2 3 5 8 ... 其前两项为1&#xff0c;第3项开始&#xff0c;每一项都是其前两项之和。 用Go实现一个简单的斐波那契计算逻辑 func fib(n int) int {if n 1 || n 2 {return 1}return fib(n-1) fib(n-2) …

MySQL---视图索引

表定义&#xff1a; 学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;S…

Redis设置开机自启动

1.新建一个系统服务文件 首先输入命令&#xff1a;vi /etc/systemd/system/redis.service 进入vim后粘贴下方代码&#xff0c;注意查看地址是否一致。 ExecStart后面接的是你的redis-server的安装位置和redis配置文件的目录 [Unit] Descriptionredis-server Afternetwork.ta…