P3919 【模板】可持久化线段树 1(可持久化数组)

【模板】可持久化线段树 1(可持久化数组)

题目背景

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

2021.9.18 增添一组 hack 数据 by @panyf

标题即题意

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

题目描述

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

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

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

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

输入格式

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

第二行包含$ N $个整数,依次为初始状态下数组各位的值(依次为 $ a_i \(,\) 1 \leq i \leq N $)。

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

  1. 对于操作1,格式为$ v_i \ 1 \ {loc}_i \ {value}i \(,即为在版本\) v_i $的基础上,将 $ a_i} $ 修改为 $ {value}_i $

  2. 对于操作2,格式为$ v_i \ 2 \ {loc}i \(,即访问版本\) v_i $中的 $ a_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

题中要求修改和查询历史信息,所以要将历史信息记录下来。

但是,如果暴力的记录,时间和空间都是不允许的。

观察发现,相邻两个历史版本的差别很小(修改,只有一位不同,查询,都一样)

那么对于每个版本只需重点维护这个版本所独有的那一部分,其余部分都是相同的,可以和上一版共用同一内存。

考虑用线段树维护这个关系。

对于每个版本都开一个树,用\(rot[i]\)表示每个版本的根节点。

对于查询操作,由于不需要进行任何修改,直接让\(rot[now] = rot[lst]\)即可

对于修改操作,这个版本所独有的点就是线段树上从根节点到要修改的位置这个路径上的点

本题有点卡常,开了O2,快读

/*
P3919 【模板】可持久化线段树 1(可持久化数组)
*/
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e6+10;
inline int read()
{
	register char c = getchar(); register int x = 0 , f = 0;
	while(c < '0' || c > '9') f |= c == '-' , c = getchar();
	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
	return f ? -x : x;
}

int n , m;
int A[N];
struct node{
	node *ls , *rs;
	int val;
	node(int val = 0) : ls(NULL) , rs(NULL) , val(val) {}
}*rot[N];

void build(node *&rt , int l , int r)
{
	rt = new node;
	if(l == r) { rt->val = A[l]; return ; }
	int mid = (l + r) >> 1;
	build(rt->ls , l , mid);
	build(rt->rs , mid+1 , r);
}

void modify(node *&rt , node *lst , int l , int r , int pos , int val)
{
	rt = new node;
	if(l == r) { rt->val = val; return ; }
	int mid = (l + r) >> 1;
	if(pos <= mid)
		rt->rs = lst->rs , modify(rt->ls , lst->ls , l , mid , pos , val);
	else
		rt->ls = lst->ls , modify(rt->rs , lst->rs , mid+1 , r , pos , val);
}

int query(node *rt , int l , int r , int pos)
{
	if(l == r) return rt->val;
	int mid = (l + r) >> 1;
	if(pos <= mid)
		return query(rt->ls , l , mid , pos);
	else
		return query(rt->rs , mid+1 , r , pos);
}

int main()
{
	n = read(); m = read();
	for(int i = 1 ; i <= n ; ++i)
		A[i] = read();
	build(rot[0] , 1 , n);
	int op , pos , v , val;
	for(int i = 1 ; i <= m ; ++i)
	{
		v = read(); op = read(); pos = read();
		// scanf("%d%d%d" , &v , &op , &pos);
		if(op == 1)
		{
			val = read();
			modify(rot[i] , rot[v] , 1 , n , pos , val);
		}
		else
		{
			printf("%d\n" , query(rot[v] , 1 , n , pos));
			rot[i] = rot[v];
		}
	}
	return 0;
}
posted @ 2022-11-30 11:26  沙野博士  阅读(47)  评论(0编辑  收藏  举报