洛谷——P3919 【模板】可持久化数组(可持久化线段树/平衡树)
P3919 【模板】可持久化数组(可持久化线段树/平衡树)
题目背景
UPDATE : 最后一个点时间空间已经放大
标题即题意
有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)
题目描述
如题,你需要维护这样的一个长度为 $N$ 的数组,支持如下几种操作
在某个历史版本上修改某一个位置上的值
访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
输入输出格式
输入格式:
输入的第一行包含两个正整数 $N$,$M$, 分别表示数组的长度和操作的个数。
第二行包含NN个整数,依次为初始状态下数组各位的值(依次为 $a_i$,$1 \leq i \leq N$)。
接下来MM行每行包含3或4个整数,代表两种操作之一(ii为基于的历史版本号):
对于操作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}$的值
输出格式:
输出包含若干行,依次为每个操作2的结果。
输入输出样例
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
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
~\(≧▽≦)/~啦啦啦
身为蒟蒻的我终于入门了可持久化线段树的冰山一角,开森
可持久化线段树总结(可持久化线段树,线段树)
%%FlashHu%%,可以直接转到这位大佬的博客学习
大佬可直接转主席树乱讲
刚入门的蒟蒻对此的理解:
可持久化线段树,支持访问历史版本,修改历史版本,emmmmm
显然,你对于每一次版本都新建一颗线段树,会TLE,更会MLE,这种方法行不通
观察发现,当你修改一个历史版本时,即创建一个新版本,当前版本相对于历史版本改变的仅仅是那个点所影响的那条链,
emmmmm貌似就这么多了
$get$到新技能,非递归版线段树
IL void insert(int *T,int u,int l,int r,int k){
//T当前节点指针,u原版本对应节点,区间端点,所要修改位置
while(l!=r){
int mid=(l+r)>>;
*T=++tot;//新增节点,分配空间
if(k<=mid) r=mid,rc[*T]=rc[u],T=&lc[*T],u=lc[u];//如果所要修改的节点在区间中点mid左侧,说明右侧线段树没有改变,T,u更新为当前左子树,右子树连接到历史版本好了,继续
else l=mid+,lc[*T]=lc[u],T=&rc[*T],u=rc[u];//上同
}
in(val[*T=++tot]);
}
奉上代码:
#include<iostream>
#include<cstdio>
#include<cmath> #define N 10000000
#define IL inline
using namespace std; IL void in(int &x){
register char c=getchar();x=;int f=;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-'';
x*=f;
} int rt[N],lc[N],rc[N],tot,val[N]; IL void build(int &RT,int l,int r){
RT=++tot;
if(l==r) {in(val[tot]);return;}
int mid=(l+r)>>;
build(lc[RT],l,mid);
build(rc[RT],mid+,r);
}
IL void insert(int *T,int u,int l,int r,int k){
while(l!=r){
int mid=(l+r)>>;
*T=++tot;
if(k<=mid) r=mid,rc[*T]=rc[u],T=&lc[*T],u=lc[u];
else l=mid+,lc[*T]=lc[u],T=&rc[*T],u=rc[u];
}
in(val[*T=++tot]);
}
IL int ask(int t,int l,int r,int k){
while(l!=r){
int m=(l+r)>>;
if(k<=m) r=m,t=lc[t];
else l=m+,t=rc[t];
}
return val[t];
} int n,m; int main()
{
in(n),in(m);
build(rt[],,n);
for(int opt,v,loc,i=;i<=m;i++){
in(v),in(opt),in(loc);
if(opt&) insert(&rt[i],rt[v],,n,loc);
else{
printf("%d\n",ask(rt[v],,n,loc));
rt[i]=++tot;
lc[tot]=lc[rt[v]];
rc[tot]=rc[rt[v]];
}
} return ;
}
emmm,来一发递归版的
#include<iostream>
#include<cstdio>
#include<algorithm> #define N 100000000
using namespace std; int rt[N],P,lc[N],rc[N],val[N]; void build(int &R,int l,int r){
R=++P;
if(l==r) {scanf("%d",&val[P]);return;}
int mid=(l+r)>>;
build(lc[R],l,mid);
build(rc[R],mid+,r);
} void insert(int *T,int u,int l,int r,int k){
*T=++P;
if(l==r) {scanf("%d",&val[*T]);return;}
int mid=(l+r)>>;
if(k<=mid) rc[*T]=rc[u],insert(&lc[*T],lc[u],l,mid,k);
else lc[*T]=lc[u],insert(&rc[*T],rc[u],mid+,r,k);
} int ask(int t,int l,int r,int k){
int mid=(l+r)>>;
if(l==r) return val[t];
if(k<=mid) return ask(lc[t],l,mid,k);
else return ask(rc[t],mid+,r,k);
} int n,m; int main()
{
scanf("%d%d",&n,&m);
build(rt[],,n);
for(int opt,v,loc,i=;i<=m;i++){
scanf("%d%d%d",&v,&opt,&loc);
if(opt==) insert(&rt[i],rt[v],,n,loc);
else printf("%d\n",ask(rt[v],,n,loc)),rt[i]=++P,lc[rt[i]]=lc[rt[v]],rc[rt[i]]=rc[rt[v]];
} return ;
}
主席树入门
以上是主席树单点修改+单点查询的模板。
主席树才A了这一道题,太弱了吧,垃圾(我)
SP11470 TTM - To the moon
在大佬博客发现的一道题(额,又是一道模板题)
主席树区间修改+区间查询模板
主要是理解线段树标记永久化的思想 线段树标记永久化 <-可以转这位大佬的博客学习
线段树标记永久化,当你所要修改的区间包含于当前区间时,就说明所要修改区间对当前区间产生了贡献,那么当前区间直接加上这个贡献即可,只有当当前区间完全符合修改区间时,
就下方标记(不,其实是添加标记,因为不需要$pushup$),这就是区间修改
IL void Modify(int &t,int u,int l,int r,int v){
t=++tot;//添加新节点
lc[t]=lc[u],rc[t]=rc[u],cov[t]=cov[u],sum[t]=sum[u];//更新新节点
sum[t]+=v*(qr-ql+);//对此区间产生贡献,此区间直接加上此贡献
if(ql==l&&qr==r) {cov[t]+=v;return;}
int mid=(l+r)>>;
if(qr<=mid) Modify(lc[t],lc[u],l,mid,v);
else if(ql>mid) Modify(rc[t],rc[u],mid+,r,v);
else Modify(lc[t],lc[u],l,mid,ql,mid),Modify(rc[t],rc[u],mid+,r,mid+,qr);
//若所修改区间qr在mid左侧,直接转到左子树,
//若所修改区间ql在mid右侧,直接转到右子树,
//反之,同时转向左右子树
}
接下来考虑区间查询,由上到下,累加标记,若当前区间有标记,那么对查询区间肯定有影响,要累加这个标记,
最后在加上所查询区间的$sum$值即可。
LL query(LL t,LL l,LL r,LL ql,LL qr){
if(l==ql&&r==qr) return sum[t];
int mid=(l+r)>>;
LL res=cov[t]*(qr-ql+);
if(qr<=mid) res+=query(lc[t],l,mid,ql,qr);
else if(ql>mid) res+=query(rc[t],mid+,r,ql,qr);
else res+=query(lc[t],l,mid,ql,mid),res+=query(rc[t],mid+,r,mid+,qr);
return res;
}
完整代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm> #define N 10000000
#define IL inline
#define LL long long
using namespace std;
void in(LL &x){
register char c=getchar();x=;LL f=;
while(!isdigit(c)){if(c=='-') f=-;c=getchar();}
while(isdigit(c)){x=x*+c-'';c=getchar();}
x*=f;
} LL rt[N],lc[N],rc[N],tot,sum[N],cov[N]; IL void build(LL &t,LL l,LL r){
t=++tot;
if(l==r){in(sum[t]);return;}
LL m=(l+r)>>;
build(lc[t],l,m);
build(rc[t],m+,r);
sum[t]+=sum[lc[t]]+sum[rc[t]];
}
IL void Modify(LL &t,LL u,LL l,LL r,LL ql,LL qr,LL v){
t=++tot;
lc[t]=lc[u],rc[t]=rc[u],cov[t]=cov[u],sum[t]=sum[u];
sum[t]+=v*(qr-ql+);
if(ql==l&&qr==r) {cov[t]+=v;return;}
LL mid=(l+r)>>;
if(qr<=mid) Modify(lc[t],lc[u],l,mid,ql,qr,v);
else if(ql>mid) Modify(rc[t],rc[u],mid+,r,ql,qr,v);
else Modify(lc[t],lc[u],l,mid,ql,mid,v),Modify(rc[t],rc[u],mid+,r,mid+,qr,v);
}
LL query(LL t,LL l,LL r,LL ql,LL qr){
if(l==ql&&r==qr) return sum[t];
int mid=(l+r)>>;
LL res=cov[t]*(qr-ql+);
if(qr<=mid) res+=query(lc[t],l,mid,ql,qr);
else if(ql>mid) res+=query(rc[t],mid+,r,ql,qr);
else res+=query(lc[t],l,mid,ql,mid),res+=query(rc[t],mid+,r,mid+,qr);
return res;
} LL n,m; int main()
{
in(n),in(m);
build(rt[],,n);
LL T=;
for(LL l,r,d,i=;i<=m;i++){
char x;
cin>>x;
if(x=='B'){
in(d);
T=d;
tot=rt[T+]-;//回收空间
}else{
in(l),in(r);
if(x=='C') in(d),++T,Modify(rt[T],rt[T-],,n,l,r,d);
if(x=='Q') printf("%lld\n",query(rt[T],,n,l,r));
if(x=='H') in(d),printf("%lld\n",query(rt[d],,n,l,r));
}
} return ;
}
只会抄题解,抄完之后还镇定自若地跟你们瞎$BB$地蒟蒻。qwq。。。
主席树总结(经典区间第k小问题)(主席树,线段树)(下一道模板题)
关于主席树的入门,讲解和题单
洛谷——P3919 【模板】可持久化数组(可持久化线段树/平衡树)的更多相关文章
- 洛谷 P3919 【模板】可持久化数组(可持久化线段树/平衡树)-可持久化线段树(单点更新,单点查询)
P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目背景 UPDATE : 最后一个点时间空间已经放大 标题即题意 有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树
洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...
- 洛谷P3919 【模板】可持久化数组 [主席树]
题目传送门 可持久化数组 题目描述 如题,你需要维护这样的一个长度为 $N$ 的数组,支持如下几种操作 在某个历史版本上修改某一个位置上的值 访问某个历史版本上的某一位置的值 此外,每进行一次操作(对 ...
- 洛谷P3919 【模板】可持久化数组(可持久化线段树/平衡树)
题目背景 UPDATE : 最后一个点时间空间已经放大 标题即题意 有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集) 题目描述 如题,你需要维护这样的一个长度为 N 的数组, ...
- 洛谷P3375 [模板]KMP字符串匹配
To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- 洛谷.5283.[十二省联考2019]异或粽子(可持久化Trie 堆)
LOJ 洛谷 考场上都拍上了,8:50才发现我读错了题=-= 两天都读错题...醉惹... \(Solution1\) 先求一遍前缀异或和. 假设左端点是\(i\),那么我们要在\([i,n]\)中找 ...
- 【AC自动机】洛谷三道模板题
[题目链接] https://www.luogu.org/problem/P3808 [题意] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. [题解] 不再介绍基础知识了,就是裸的模 ...
随机推荐
- 蓝桥 PREV-34 历届试题 矩阵翻硬币
历届试题 矩阵翻硬币 时间限制:1.0s 内存限制:256.0MB 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第 ...
- c# Java 微信红包算法
int total_money_cent = 1000; // 红包总金额 单位:分 int total_people = 8; // 抢红包总人数 int[] array = new int[tot ...
- Application Warm-up Module IIS7.5 也有Warm Up功能,让ASP.NET 第一次Request不变慢
Application Warm-up Module: 應用程式的暖機代理人 http://www.microsoft.com/taiwan/technet/iis/expand/Applicatio ...
- POJ2115 C-Loop
传送门 这道题是求解不定方程的一道好练习题. 题目描述的很诡异……还说什么k进制,其实就是要求一个数A,每次加C,问到B要加多少次,所有的数对2k取模. 也就是说我们能列出如下方程:A+xC ≡ B ...
- TypeError: expected bytes-like object, not str
报错内容:TypeError: expected bytes-like object, not str 例: a = base64.b64encode(temp) 改为: a = base64.b64 ...
- 函数bsxfun,两个数组间元素逐个计算的二值操作
转自http://www.cnblogs.com/rong86/p/3559616.html 函数功能:两个数组间元素逐个计算的二值操作 使用方法:C=bsxfun(fun,A,B) 两个数组A合B间 ...
- PHP面向对象技术(全面讲解)
作者:高洛峰 来源:<PHPer>杂志 1.面向对象的概念 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机 ...
- Mybatis Generator插件升级版
一.目的: 1. *mapper.java 文件名称 改为*DAO.java2. mapper以及mapper.xml 重复执行,只会覆盖原模板方法,不会覆盖自定义方法3. 实体类添加中文注释 二.步 ...
- JMeter配置MongoDB
1.启动JMeter,右键添加->配置文件->MongoDB Source Config. 注意:JMeter 3.0以上版本去掉了此配置项,可以从低版本处拷贝. 2.设置MongoDB配 ...
- ASP.NET 之页面重定向和传值
在开发 ASP.NET 网站时,经常需要从一个网页重定向(导航)到另一个网页,同时希望能够将信息从源页传递到目标页.例如,如果你正在开发一个保险网站,需要用一个页面来收集基本信息(用户信息.保险产品信 ...