社区讨论
关于我口胡出来一种可以维护带修改的前缀和(max,异或等)的数据结构这件事
灌水区参与者 18已保存回复 26
讨论操作
快速查看讨论及其快照的属性,并进行相关操作。
- 当前回复
- 26 条
- 当前快照
- 1 份
- 快照标识符
- @lo25lqhz
- 此快照首次捕获于
- 2023/10/23 08:22 2 年前
- 此快照最后确认于
- 2023/11/03 08:40 2 年前
这种 lj 数据结构能将原本的前缀和 查询和 修改的缺陷平衡至 修改与查询。而且,如果您嫌麻烦的话,也可以实现一个 查询, 修改前缀和的数据结构。缺点是空间复杂度会变大。
本文是我家养的宠物猪jc我本人后乱发的,求各位大佬轻喷
以下是数据结构的具体内容:
如果我们要维护一个序列 的前缀和,我们可以直接用一个 数组保存。但是这种方法简陋且不具有美感。
我们可以以整个序列上的元素建造一棵二叉树。请注意,它不同于线段树和树状数组,它仅仅是将 作为根节点, 从左到右作为第一层, 从左到右作为第二层,以此类推。它是一棵完全二叉树。
二叉树上的点有点权,边有边权。边权不会改动。一个点 到根节点路径上所有边的边权和和点权和就是 的初始前缀和。
先不讨论点权,它初始是 。
边权具体是多少? 还是以 为例子, 到它的父亲 的边权就是区间 的和。边权均可以用 的预处理求。
很显然,“一个点 到根节点路径上所有边的边权和就是 的初始前缀和” 这句话是正确的。我们仅仅是将一个前缀和分成了 段。
假如我们要修改一个值(例如加减 ),那么此时将会有一些与其相对应的边权需要改动。但是由于这些边都连向同一或同二层,我们显然有一种更加巧妙的方法修改:
我们将两层上需要修改的入边分别考虑。每一层上的点都是连续的。于是将这些点分为 段,每段长度都可以用 表示, 为自然数。可以 求出每一段的 ,我们将这些 的结果增加深度数组上的点权。
例如,我们修改一个点 时,分出来两层 与 、假设我们要处理 的,我们又可以分为两段 与 。此时只需要修改 与 的点权就行了。
但是这样有一个问题:我们要查询 的前缀和,仍然会遍历到 。而 是不包括 的!
所以对于每一个点,开辟一个深度数组的空间,用树状数组维护 或者暴力维护 。我们修改点权时,记录使它修改的那一层的深度,并在深度数组上记录点权的修改情况。当我们遍历到修改过的点时,只需要看起始深度是否比修改深度大就行了。
可能这并没有什么用。但其实 的时间复杂度是极小的。且它可以用同样的方法维护带修改的前缀max,前缀异或,以及一些奇奇怪怪的前缀。
如果还有可以优化的地方,欢迎苣佬们给蒟蒻提一些意见哇QAQ
代码:
修改
CPPvoid update(int p,int zz){
int lx=p,rx=rblock[deep[p]+1],ly=lblock[deep[willam_tree[p-1].rson]+1]==0 ? mxn2/2-1 : lblock[deep[willam_tree[p-1].rson]+1],ry=min(n-1,willam_tree[p-1].rson);
if(deep[p]+1==deep[willam_tree[p-1].rson]+1){
willam_tree[0].pointnum[deep[p]+1]+=zz;
return;
}
if(deep[p]+1==maxdeep){
ly=ry+1;
}
for(int z=mxn2,r=cntn2;z>=1 && lx<=rx;z/=2,r--){
if(rx-z>=lx-1){
if(r==0){
willam_tree[rx-z+1].pointnum[deep[p]+1]+=zz;
}else{
willam_tree[fa[rx-z+1][r-1]].pointnum[deep[p]+1]+=zz;
}
rx-=z;
}
}
for(int z=mxn2,r=cntn2;z>=1 && ly<=ry;z/=2,r--){
if(ly+z<=ry+1){
if(r==0){
willam_tree[ly].pointnum[deep[willam_tree[p-1].rson]+1]+=zz;
}else{
willam_tree[fa[ly][r-1]].pointnum[deep[willam_tree[p-1].rson]+1]+=zz;
}
ly+=z;
}
}
}
查询:
CPPint ask(int x,int firsts){
if(x==-1){
return -a[0];
}
int anss=0;
for(int i=1;i<=firsts;i++){
if(willam_tree[x].pointnum[i]!=0){
anss+=willam_tree[x].pointnum[i];
}
}
if(x==0){
return anss;
}
return ask(fa[x][0],firsts)+anss+willam_tree[x].fanum;
}
回复
共 26 条回复,欢迎继续交流。
正在加载回复...