可持续化数组

可持续化数组

1.算法分析

3.典型例题

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

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

  1. 在某个历史版本上修改某一个位置上的值: v i   1   l o c i   v a l u e i v_i\ 1\ loc_i\ value_i vi 1 loci valuei, 即为在版本 v i v_i vi的基础上,将 a l o c i a_{{loc}_i} aloci修改为 v a l u e i {value}_i valuei

  2. 访问某个历史版本上的某一位置的值: v i   2   l o c i v_i \ 2 \ {loc}_i vi 2 loci,即访问版本 v i v_i vi中的 a l o c i a_{{loc}_i} aloci的值,生成一样版本的对象应为 v i vi vi

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组) 1 < = n , m < = 1 0 6 , 1 < = l o c i < = N , 0 < = v i < i , − 1 e 9 < = a i , v a l u e i < = 1 e 9 1<=n,m<=10^6,1<=loc_i<=N,0<=v_i<i,-1e9<=a_i,value_i<=1e9 1<=n,m<=106,1<=loci<=N,0<=vi<i,1e9<=ai,valuei<=1e9

题解:

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
int a[N];
struct Node {
    int l, r,
        val;  // l和r为左右子树的节点,val为记录的数字,但是val只有叶节点才使用
} hjt[N * 40];
int cnt, root[N];  // cnt为计数变量,root[i]记录版本号为i的线段树的根节点

// 建树
void build(int l, int r, int &now) {
    now = ++cnt;  // 生成新结点
    if (l == r)   // 如果到叶节点
    {
        hjt[now].val = a[l];  // 赋值
        return;
    }

    int m = (l + r) >> 1;         // 求中点
    build(l, m, hjt[now].l);      // 建左子树
    build(m + 1, r, hjt[now].r);  // 建右子树
}

// 修改操作
void modify(int l, int r, int ver, int &now, int &pos, int &num) {
    hjt[now = ++cnt] = hjt[ver];  // 复制一个原来的版本
    if (l == r)                   // 修改叶节点
    {
        hjt[now].val = num;
        return;
    }
    int m = (l + r) >> 1;  // 中点
    if (pos <= m)
        modify(l, m, hjt[ver].l, hjt[now].l, pos,
               num);  // 如果pos小于等于中点,说明在左子树,修改左子树
    else
        modify(m + 1, r, hjt[ver].r, hjt[now].r, pos, num);  // 修改右子树
}

// 询问操作
int query(int l, int r, int now, int &pos) {
    if (l == r) return hjt[now].val;  // 到达叶节点
    int m = (l + r) >> 1;             // 求中点
    if (pos <= m)
        return query(l, m, hjt[now].l,
                     pos);  // 如果要查询的位置比中点小,查询左子树
    else
        return query(m + 1, r, hjt[now].r, pos);  // 否则查询右子树
}

int main(int argc, char const *argv[]) {
    int n, m, ver, opt, x, y;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];  // 读入数字
    build(1, n, root[0]);                      // 建树,0为初始化版本
    for (int i = 1; i <= m; i++)               // 读入询问
    {
        cin >> ver >> opt;
        switch (opt) {
            case 1:
                cin >> x >> y;
                modify(1, n, root[ver], root[i], x, y);  // 修改
                break;
            case 2:
                cin >> x;
                cout << query(1, n, root[ver], x) << endl;  // 查询
                root[i] = root[ver];                        // 记录新版本
                break;
        }
    }
    return 0;
}
### 回答1: 好的,以下是一份可持续线段树的代码: ``` #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m,op,l,r,c,w[N],root[N],cnt; struct node{ int l,r,sum; }t[N*40]; int build(int l,int r){ int now=++cnt; t[now].sum=0; if(l==r){ return now; } int mid=(l+r)>>1; t[now].l=build(l,mid); t[now].r=build(mid+1,r); return now; } int update(int pre,int l,int r,int x,int k){ int now=++cnt; t[now]=t[pre]; t[now].sum+=k; if(l==r){ return now; } int mid=(l+r)>>1; if(x<=mid){ t[now].l=update(t[pre].l,l,mid,x,k); }else{ t[now].r=update(t[pre].r,mid+1,r,x,k); } return now; } int query(int x,int y,int l,int r,int k){ if(l>=k&&r<=k){ return t[y].sum-t[x].sum; } int mid=(l+r)>>1,ans=0; if(k<=mid){ ans+=query(t[x].l,t[y].l,l,mid,k); } if(k>mid){ ans+=query(t[x].r,t[y].r,mid+1,r,k); } return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ scanf("%d",&w[i]); } sort(w+1,w+n+1); int tot=unique(w+1,w+n+1)-w-1; root[0]=build(-1e9,1e9); for(int i=1;i<=n;i++){ root[i]=update(root[i-1],-1e9,1e9,w[i],1); } while(m--){ scanf("%d%d%d",&op,&l,&r); if(op==1){ scanf("%d",&c); int k=lower_bound(w+1,w+tot+1,c)-w-1; printf("%d\n",query(root[l-1],root[r],-1e9,1e9,k)); }else{ ### 回答2: 可持续线段树是线段树的一种改进方法,能够在原线段树的基础上支持多次修改和查询某个历史版本的功能。下面是一份简单的可持续线段树的代码: ```python class Node: def __init__(self, value): self.value = value self.left = None self.right = None class PersistentSegmentTree: def __init__(self, arr): self.version = 0 # 当前版本号 self.arr = arr # 初始数组 self.versions = {0: self.build_tree(0, len(self.arr) - 1, self.arr)} # 版本字典,初始版本构建一棵线段树 def build_tree(self, left, right, arr): if left == right: return Node(arr[left]) # 叶节点 mid = (left + right) // 2 root = Node(0) root.left = self.build_tree(left, mid, arr) root.right = self.build_tree(mid+1, right, arr) root.value = root.left.value + root.right.value # 内部节点存储区间和 return root def update(self, index, value): self.version += 1 new_tree = self.update_recursive(self.versions[self.version - 1], 0, len(self.arr) - 1, index, value) self.versions[self.version] = new_tree def update_recursive(self, root, left, right, index, value): if left == right: return Node(value) mid = (left + right) // 2 new_node = Node(0) if index <= mid: new_node.left = self.update_recursive(root.left, left, mid, index, value) new_node.right = root.right else: new_node.left = root.left new_node.right = self.update_recursive(root.right, mid+1, right, index, value) new_node.value = new_node.left.value + new_node.right.value return new_node def query(self, left, right, version): return self.query_recursive(self.versions[version], 0, len(self.arr) - 1, left, right) def query_recursive(self, root, left, right, query_left, query_right): if query_left <= left and query_right >= right: # 完全包含,直接返回该节点的值 return root.value if query_left > right or query_right < left: # 完全不相交,返回0 return 0 mid = (left + right) // 2 return self.query_recursive(root.left, left, mid, query_left, query_right) + \ self.query_recursive(root.right, mid+1, right, query_left, query_right) ``` 上述代码描述了一个基于数组的可持续线段树,其中包含了构建线段树、更新节点和查询区间的功能。对于每个历史版本,使用版本号作为索引存储在versions字典中。该线段树的时间复杂度为O(logn),其中n为数组长度。 ### 回答3: 可持续线段树是一种用于解决在线算法问题的数据结构,它可以支持对原始线段树的修改,并能够保留每个版本的历史状态。以下是一个简的可持续线段树的代码实现: ```python class Node: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right class PersistentSegmentTree: def __init__(self, nums): self.nums = nums self.roots = [self.build(0, len(nums) - 1)] def build(self, left, right): if left == right: return Node(self.nums[left]) mid = (left + right) // 2 left_child = self.build(left, mid) right_child = self.build(mid + 1, right) return Node(left_child.val + right_child.val, left_child, right_child) def update(self, root_index, index, val): new_root = self.clone(self.roots[root_index]) self._update(new_root, index, val, 0, len(self.nums) - 1) self.roots.append(new_root) def _update(self, node, index, val, left, right): if left == right: node.val = val return mid = (left + right) // 2 if index <= mid: node.left = self.clone(node.left) self._update(node.left, index, val, left, mid) else: node.right = self.clone(node.right) self._update(node.right, index, val, mid + 1, right) node.val = node.left.val + node.right.val def query(self, root_index, start, end): return self._query(self.roots[root_index], start, end, 0, len(self.nums) - 1) def _query(self, node, start, end, left, right): if start > right or end < left: return 0 if start <= left and end >= right: return node.val mid = (left + right) // 2 return self._query(node.left, start, end, left, mid) + self._query(node.right, start, end, mid + 1, right) def clone(self, node): return Node(node.val, node.left, node.right) ``` 该代码实现了一个可持续线段树的类`PersistentSegmentTree`,主要包括以下几个方法: - `__init__(self, nums)`: 类的初始方法,接受一个包含初始数据的列表。 - `build(self, left, right)`: 递归构建线段树的方法。 - `update(self, root_index, index, val)`: 对线段树进行更新的方法,指定根节点索引、要更新的位置和新的值。 - `_update(self, node, index, val, left, right)`: 实际进行更新的递归方法。 - `query(self, root_index, start, end)`: 查询指定范围内元素和的方法,指定根节点索引、开始和结束位置。 - `_query(self, node, start, end, left, right)`: 实际进行查询的递归方法。 - `clone(self, node)`: 复制节点的方法。 以上是一个基本的可持续线段树的代码实现,可以根据具体需求进行适当的修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值