首页 > 编程知识 正文

速写风景简单超清图,超简单简笔画

时间:2023-05-04 06:50:35 阅读:47441 作者:2102

原文链接:这篇由https://www.cn blogs.com/Jason 2003/p/9676729.html参考的大人物文章,不过增添了一点自己的理解。 有几个地方有问题。 分段树(RMQ问题)分段树是二叉树,本质上我们用一个二叉树存储一条线段,树的每个节点记录一个区间的长度。

举一个例子

(照片从网上删除入侵)

如图所示,制作二叉树的各节点,保存区间的长度。 说明一下,根节点存储了1——4的和,他的左儿子存储了1——2的和,右儿子存储了——4的和。 从上面的介绍可以看出,一个节点的值等于他的左儿子和右儿子之和。

要学习什么是段木,就必须知道段木的制作方法。

)1)要创建段树,首先需要一个用于存储树的结构树。 树中包含tree_l。 tree_r表示树的左边界和右边界,并用sum记录存储在该节点上的值。 制作线条木依靠tree [ I ].sum=tree [2* I ].sumtree [2* i1 ]。 然后可以递归地种树

在此显示创建线段树的代码;

语音构建(Inti; int l; intr(//递归树tree[i].l=l; tree[i].r=r; //节点左右边界if(L==R ) ) /判断是否是叶节点的tree[i].sum=input[l]; //input数组是您输入的数组;返回; (intmid=) LR )/2; //从记述中可以看出左右儿子的得分值是远离中心点的bulid(2*I,l,mid )。 //构建左子木build (2* i1,mid 1,r ); //右部分树tree [ I ].sum=tree [2* I ].sumtree [2* i1 ].sum; //分层递归地得到各节点的值}

注:内联树使用数组存储值,并且需要大量数据,因此tree的数组大小通常是input的4倍。

做好线条木后,我们开始用它。

)2)单点修改和区间查询区间查询:

还是以刚才的图为例,例如,如果我们研究1——3区间的和,就会发现首先与根节点相比,虽然没有涵盖一切,但它与根节点的左右儿子交叉,所以先去左儿子那里,左儿子则是1——44443 我们直接回到这个节点的值3,然后我们到右边的儿子那里,发现虽然没有完全覆盖右边的儿子,但是和他左边的儿子有交集。 然后,这样得到了1——3的值

总结区间查询的操作

1、有线段树的区间完全被我们要求的区间复盖的情况下,我们返回那个区间的值

2、区间不能完全覆盖的情况下,我们去他左右的儿子,看它和想要的区间交叉,跳到那个区间反复操作

代码实现:

intseach(intI; int l; int r ) (/递归调查l和r,是判断我求出的区间if(tree[I].r=rtree[I].l=l ) ) /该区间是否完全覆盖的返回树[ I ].sum (if ) tree[2*I].r=L ) s=seach )2*I,l,r ); //判断是否存在与左儿子的交点(if(tree[2*R1].L=R ) ) s=seach )2*I1,l,r ); //判断是否与右儿子有交点(} return s; //s记录区间的值(一点修正

一点修改很简单。 要修改一个点,请从根节点开始,然后确定该点是在他的左儿子还是在右儿子。 然后跳到那个区间。 递归去找,知道找到了那一点就返回,修正所有通过点结束。 参照图像。

代码如下。

voidadd(intI,int dis,int k ) {//dis是要修改的,而k是值if(tree[I].l==tree[I].r ) /判断是否是叶节点的tree [ I ]. }if(dis=tree[2*I].r ) add )2*I,dis,k ); //判断修正的点在该区间,如果在该区间,则跳至该elseadd(2*I1,dis,k )。 tree [ I ].sum=tree [ I *2].sumtree [ I * 21 ].sum; (3)区间修改和单查询

虽然区间修改和单查询操作实际上与以前的操作相同,但可以看到某个问题区间修改的目的是为了后续查询。 但是,在具体问题上,我们修正的区间中有我们不能使用的,也有一些线段树中有不能使用的区间。 但是,我们在进行区间修正的时候修正那个。 这样做会增加浪费时间的复杂性。 所以为了避免这个问题,我们引入了新的知识“懒惰的标志”

)4)懒惰标志hmdmg他懒惰,只有我们推他走了。 丝段树为什么

这莫牛逼,就是因为存在这个懒标记。

接下来我们使用懒标记来对区间修改和单点查询进行操作

区间修改:我们遵循以下原则

1、如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)

2、如果没有完全覆盖,则先下传懒标记

3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

演示代码如下:

void add(int i,int l,int r,int k){ if(tree[i].l>=l&&tree[i].r<=r){        tree[i].sum+=k*(tree[i].r-tree[i].l+1);        tree[i].淡淡的摩托+=k;   }    push_down(i);    if(tree[2*i].r>=l){        add( 2*i, l, r, k);   }    if(tree[2*i+1].l<=r){        add(2*i+1,l,r,k);   }    tree[i].sum=tree[2*i].sum+tree[2*i+1].sum;    return;}

其中的push_down的·作用就是将父亲的淡淡的摩托归零,然后将淡淡的摩托传递给左右儿子。线段树的精髓就是这个所以很重要。

演示代码如下:

void push_down(int i){    if(tree[i].淡淡的摩托!=0){ tree[2*i].sum+=tree[i].淡淡的摩托*(tree[2*i].r-tree[2*i].l+1);        tree[2*i].淡淡的摩托+=k;        tree[2*i+1].sum+=tree[i].淡淡的摩托*(tree[2*i+1].r-tree[2*i+1].l+1);        tree[2*i+1].淡淡的摩托+=k;        tree[i].k=0;   }    return ;}

具体解释一下,当进行区间修改的时候我们没必要将所有的tree节点都进行修改,比如我们修改1——3区间时,由于1——2区间以及3——3区间就已经可以将1——3区间全部代替那我们就没有必要将1——1区间和2——2区间的值进行修改,但是未来我们很有可能能回被要求查询到1——1和2——2区间的值,所以我们就用淡淡的摩托标记去记录我们曾经所要进行的操作。等我们要进行操作的时候在将其加上去。这样就可以节约一点时间。

由于单点查询过于简单我就直接展示有了淡淡的摩托标记的区间查询的代码吧

代码展示:

int seach(int i;int l;int r){    if(tree[i].l>=l&&tree[i].r<=r){        return tree[i].sum;   }    push_down(i);    if(tree[2*i].r>=l){ s+=seach(2*i,l,r);   }    if(tree[2*i+1].l<=r){        s+=seach(2*i+1,l,r);   }    return s;}

线段树就先学到这吧,等以后有心情了在学!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。