可持久化线段树
可持久化线段树
可以询问修改过的历史版本
【模板】可持久化线段树 1(可持久化数组)
题目背景
UPDATE : 最后一个点时间空间已经放大
2021.9.18 增添一组 hack 数据 by @panyf
标题即题意
有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)
题目描述
如题,你需要维护这样的一个长度为 \(N\) 的数组,支持如下几种操作
-
在某个历史版本上修改某一个位置上的值
-
访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
输入格式
输入的第一行包含两个正整数 \(N, M\), 分别表示数组的长度和操作的个数。
第二行包含\(N\)个整数,依次为初始状态下数组各位的值(依次为 \(a_i\),\(1 \leq i \leq N\))。
接下来\(M\)行每行包含3或4个整数,代表两种操作之一( \(i\) 为基于的历史版本号):
-
对于操作1,格式为\(v_i \ 1 \ {loc}_i \ {value}_i\),即为在版本\(v_i\)的基础上,将 \(a_{{loc}_i}\) 修改为 \({value}_i\)
-
对于操作2,格式为\(v_i \ 2 \ {loc}_i\),即访问版本\(v_i\)中的 \(a_{{loc}_i}\)的值,生成一样版本的对象应为\(vi\)
输出格式
输出包含若干行,依次为每个操作2的结果。
样例 #1
样例输入 #1
5 10
59 46 14 87 41
0 2 1
0 1 1 14
0 1 1 57
0 1 1 88
4 2 4
0 2 5
0 2 4
4 2 1
2 2 2
1 1 5 91
样例输出 #1
59
87
41
87
88
46
提示
数据规模:
对于30%的数据:\(1 \leq N, M \leq {10}^3\)
对于50%的数据:\(1 \leq N, M \leq {10}^4\)
对于70%的数据:\(1 \leq N, M \leq {10}^5\)
对于100%的数据:\(1 \leq N, M \leq {10}^6, 1 \leq {loc}_i \leq N, 0 \leq v_i < i, -{10}^9 \leq a_i, {value}_i \leq {10}^9\)
经测试,正常常数的可持久化数组可以通过,请各位放心
数据略微凶残,请注意常数不要过大
另,此题I/O量较大,如果实在TLE请注意I/O优化
询问生成的版本是指你访问的那个版本的复制
样例说明:
一共11个版本,编号从0-10,依次为:
0 : 59 46 14 87 41
1 : 59 46 14 87 41
2 : 14 46 14 87 41
3 : 57 46 14 87 41
4 : 88 46 14 87 41
5 : 88 46 14 87 41
6 : 59 46 14 87 41
7 : 59 46 14 87 41
8 : 88 46 14 87 41
9 : 14 46 14 87 41
10 : 59 46 14 87 91
std
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+9;
int n,m,a[N],root[N];
struct tree
{
int ls,rs,val;
tree()
{
ls = rs = val = 0;
}
#define ls(x) t[x].ls
#define rs(x) t[x].rs
#define val(x) t[x].val
}t[N*23];
int tot;
int clone(int p)
{
tot++;
t[tot] = t[p];//复制信息
return tot;
}
int build(int p,int l_,int r_)
{
p = ++tot;
if(l_ == r_)
{
val(p) = a[l_];
return p;
}
int mid = (l_+r_)>>1;
ls(p) = build(ls(p),l_,mid);
rs(p) = build(rs(p),mid+1,r_);
return p;记得return
}
int modify(int p,int l_,int r_,int x,int v)
{
p = clone(p);
if(l_ == r_)
{
val(p) = v;
return p;
}
int mid = (l_+r_)>>1;
if(x <= mid)ls(p) = modify(ls(p),l_,mid,x,v);
else rs(p) = modify(rs(p),mid+1,r_,x,v);
return p;//与普通线段树不同 记得return
}
int query(int p,int l_,int r_,int x)
{
if(l_ == r_)return val(p);
int mid = (l_+r_)>>1;
if(x <= mid)return query(ls(p),l_,mid,x);
else return query(rs(p),mid+1,r_,x);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)scanf("%d",&a[i]);
root[0] = build(0,1,n);
for(int i = 1;i <= m;i++)
{
int vi,op,x,v;
scanf("%d%d%d",&vi,&op,&x);
if(op == 1)
{
scanf("%d",&v);
root[i] = modify(root[vi],1,n,x,v);//会产生一个新的版本记得记下来
}
else
{
printf("%d\n",query(root[vi],1,n,x));
root[i] = root[vi];//每次询问也会产生一个新的版本
}
}
return 0;
}
可以利用前缀和的思想来求区间第k大的数(可持久化权值线段树)
【模板】可持久化线段树 2
题目背景
这是个非常经典的可持久化权值线段树入门题——静态区间第 \(k\) 小。
数据已经过加强,请使用可持久化权值线段树。同时请注意常数优化。
题目描述
如题,给定 \(n\) 个整数构成的序列 \(a\),将对于指定的闭区间 \([l, r]\) 查询其区间内的第 \(k\) 小值。
输入格式
第一行包含两个整数,分别表示序列的长度 \(n\) 和查询的个数 \(m\)。
第二行包含 \(n\) 个整数,第 \(i\) 个整数表示序列的第 \(i\) 个元素 \(a_i\)。
接下来 \(m\) 行每行包含三个整数 $ l, r, k$ , 表示查询区间 \([l, r]\) 内的第 \(k\) 小值。
输出格式
对于每次询问,输出一行一个整数表示答案。
样例 #1
样例输入 #1
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
样例输出 #1
6405
15770
26287
25957
26287
提示
样例 1 解释
\(n=5\),数列长度为 \(5\),数列从第一项开始依次为\(\{25957, 6405, 15770, 26287, 26465\}\)。
- 第一次查询为 \([2, 2]\) 区间内的第一小值,即为 \(6405\)。
- 第二次查询为 \([3, 4]\) 区间内的第一小值,即为 \(15770\)。
- 第三次查询为 \([4, 5]\) 区间内的第一小值,即为 \(26287\)。
- 第四次查询为 \([1, 2]\) 区间内的第二小值,即为 \(25957\)。
- 第五次查询为 \([4, 4]\) 区间内的第一小值,即为 \(26287\)。
数据规模与约定
- 对于 \(20\%\) 的数据,满足 \(1 \leq n,m \leq 10\)。
- 对于 \(50\%\) 的数据,满足 \(1 \leq n,m \leq 10^3\)。
- 对于 \(80\%\) 的数据,满足 \(1 \leq n,m \leq 10^5\)。
- 对于 \(100\%\) 的数据,满足 \(1 \leq n,m \leq 2\times 10^5\),\(|a_i| \leq 10^9\),\(1 \leq l \leq r \leq n\),\(1 \leq k \leq r - l + 1\)。
std
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+9;
int n,m,a[N],b[N];
map<int,int>book;
int root[N],tot;
struct tree
{
int ls,rs,sum;
tree()
{
ls = rs = sum = 0;
}
#define ls(x) t[x].ls
#define rs(x) t[x].rs
#define sum(x) t[x].sum
}t[N*23];
void iscre()
{
sort(b+1,b+1+n);
int k = unique(b+1,b+1+n)-b-1;
for(int i = 1;i <= n;i++)
{
int t = lower_bound(b+1,b+1+k,a[i])-b;
book[t] = a[i];
a[i] = t;
}
}
void pushup(int p)
{
sum(p) = sum(ls(p)) + sum(rs(p));
}
int clone(int p)
{
t[++tot] = t[p];
return tot;
}
int build(int p,int l_,int r_)
{
p = ++tot;
if(l_ == r_)return p;
int mid = (l_+r_)>>1;
ls(p) = build(ls(p),l_,mid);
rs(p) = build(rs(p),mid+1,r_);
//pushup(p);
return p;
}
int modify(int p,int l_,int r_,int x)
{
p = clone(p);
if(l_ == r_)
{
sum(p)++;
return p;
}
int mid = (l_+r_)>>1;
if(x <= mid)ls(p) = modify(ls(p),l_,mid,x);
else rs(p) = modify(rs(p),mid+1,r_,x);
pushup(p);
return p;
}
int query(int lp,int rp,int l_,int r_,int k)
{
if(l_ == r_)return book[l_];
int x = sum(ls(rp))-sum(ls(lp));
int mid = (l_+r_)>>1;
if(k <= x)return query(ls(lp),ls(rp),l_,mid,k);
else return query(rs(lp),rs(rp),mid+1,r_,k-x);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)scanf("%d",&a[i]),b[i] = a[i];
iscre();
root[0] = build(0,1,n);
for(int i = 1;i <= n;i++)root[i] = modify(root[i-1],1,n,a[i]);
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",query(root[l-1],root[r],1,n,k));
}
return 0;
}

浙公网安备 33010602011771号