洛谷P3919 【模板】可持久化数组(可持久化线段树/平衡树)

题目背景

UPDATE : 最后一个点时间空间已经放大

标题即题意

有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)

题目描述

如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作

  1. 在某个历史版本上修改某一个位置上的值

  2. 访问某个历史版本上的某一位置的值

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

输入输出格式

输入格式:

输入的第一行包含两个正整数 N, M, 分别表示数组的长度和操作的个数。

第二行包含N个整数,依次为初始状态下数组各位的值(依次为 ai1iN)。

接下来M行每行包含3或4个整数,代表两种操作之一(i为基于的历史版本号):

  1. 对于操作1,格式为vi 1 loci valuei,即为在版本vi的基础上,将 aloci 修改为valuei

  2. 对于操作2,格式为vi 2 loci,即访问版本vi中的 aloci的值,生成一样版本的对象应为vi

输出格式:

输出包含若干行,依次为每个操作2的结果。

输入输出样例

输入样例#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%的数据:1N,M10^3

对于50%的数据:1N,M10^4

对于70%的数据:1N,M10^5

对于100%的数据:1N,M10^6,1lociN,0vi<i,10^9ai,valuei10^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

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define mid ((l+r)>>1)
 6 using namespace std;
 7 const int maxn=10000001;
 8 long long read()
 9 {
10     long long x=0,f=1;
11     char ch=getchar();
12     while(ch<'0'||ch>'9')
13     {
14         if(ch=='-')
15             f=-1;
16         ch=getchar();
17     }
18     while(ch>='0'&&ch<='9')
19     {
20         x=x*10+ch-'0';
21         ch=getchar();
22     }
23     return x*f;
24 }
25 struct node
26 {
27     int rt[maxn*10],T[maxn*2],L[maxn*2],R[maxn*2];
28     int cnt;
29     int build(int l,int r)
30     {
31         int root=++cnt;
32         if(l==r)
33         {
34             T[root]=read();
35             return root;
36         }
37         L[root]=build(l,mid);
38         R[root]=build(mid+1,r);
39         return root;
40     }
41     int update(int pre,int l,int r,int &x,int &c)
42     {
43         int root=++cnt;
44         if(l==r)
45         {
46             T[root]=c;
47             return root;
48         }
49         L[root]=L[pre];
50         R[root]=R[pre];
51 
52         if(x<=mid)
53             L[root]=update(L[pre],l,mid,x,c);
54         else
55             R[root]=update(R[pre],mid+1,r,x,c);
56         return root;
57     }
58     void query(int pre,int l,int r,int& x)
59     {
60         if(l==r)
61         {
62             printf("%d\n",T[pre]);
63             return;
64         }
65         if(x<=mid)
66             query(L[pre],l,mid,x);
67         else
68             query(R[pre],mid+1,r,x);
69     }
70 } iu;
71 int n,m,v,cd,x,y;
72 int main()
73 {
74     iu.cnt=0;
75     n=read(),m=read();
76     iu.build(1,n);
77     iu.rt[0]=1;
78     for(int i=1; i<=m; i++)
79     {
80         v=read(),cd=read(),x=read();
81         if(cd==1)
82         {
83             y=read();
84             iu.rt[i]=iu.update(iu.rt[v],1,n,x,y);
85         }
86         if(cd==2)
87         {
88             iu.rt[i]=iu.rt[v];
89             iu.query(iu.rt[v],1,n,x);
90         }
91     }
92     return 0;
93 }
View Code

 

转载于:https://www.cnblogs.com/liweilin/p/10188732.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值