首页 > 其他分享 >【学习笔记】线段树(待补)

【学习笔记】线段树(待补)

时间:2024-04-10 21:38:15浏览次数:18  
标签:lazy point 待补 线段 tree 笔记 int 区间 data

零、写在前面的话

我发现学习笔记是真的有必要的。很多比赛甚至做题的时候,学过的算法就出现在题目里面,然而我却忘记了之前对这个算法的深入理解,甚至忘了这个算法怎么打,更甚者,看不出来这个题目可以使用这种算法解决。

为了防止这种情况再度出现,我决定对自己的任何学过的算法写笔记,即使是低级的算法。同时,我会不定时翻看自己的笔记,笔记完全原创,希望留下更加深刻的印象。

一、线段树入门

1. 定义 & 作用

线段树是什么?一个可以用来维护许多区间操作的强大数据结构。它可以实现许多操作,例如区间加减乘除等。

2. 主要思想 & 实现

线段树,顾名思义,把一个序列分成很多个小线段。举个例子,有一个长度为 10 的序列,我们把它分成下面这个形状:

就是一直对半分下去,直到只有一个数。这时,许多操作就变得方便起来了。

2.1 查询

当我想查询一段数的和时,例如 3~7,我可以这么做:首先我把这个查询的任务交给区间 1~10,区间 1~10 进行处理后发现,有一段在他的左边,有一段在他的右边。于是,它交给它的左儿子区间一个任务:查询 3~5。再交给它的右儿子区间一个任务:查询 6~7,然后再把两段的和加起来就是答案,以此类推。当我发现我的区间被目标区间完全包含,就可以直接加上我这一段的值了。

于是,我们成功完成了区间查询操作。

2.2 单点修改

当我想修改一个数时,比如 4,我可以这么做:首先我把这个修改的任务交给区间 1~10,区间 1~10 进行处理后发现,4 在它的左儿子“管辖范围”内。于是,他把这个任务交给了左儿子 1~5,以此类推,直到找到 4 为止。

于是,我们成功完成了单点修改操作。

2.3 区间修改

这里需要讲到线段树的精髓了:lazytag,这个东西可以有很多的应用。我觉得,可以把这个东西理解为一个标记,一个支持区间修改的标记,而这个标记最常见的应用就是区间加。

首先,我们赋予每个区间一个值,称之为 lazytag。然后,在我想要区间修改时,找到我所需要的区间(像查询的方式),改变它的 lazytag 值以及存储的值。

重点来了,改变了之后,你什么也不需要做。没错,只需要改变一个值而不是你的区间中的每个数的值,也不需要改变你的子区间的值。

然后,当我们需要用到这个区间以及这个区间的子区间时(例如查询、修改时),我们再把 lazytag 的值“下放”。什么是下放呢?修改自己两个儿子的 lazytag 值,按照你的意愿。然后再修改两个儿子存储的值(它可能是最大值、和什么的)

于是,我们成功完成了区间修改操作。

二、线段树进阶

主要放一些例题,从中理解线段树的奥秘吧。

1. \(\color{#3498DB}\text{ P2146 [NOI2015] 软件包管理器}\)

树链剖分的模板题啊。这里用到的比较妙的一个是区间重置。把区间中的数设置为一个数。

附、模板

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010;
int n,a[N];
struct data
{
	int data,l,r,lazy;
}tree[N*8];
void lazytag(int point)
{
	if(tree[point].lazy)
	{
		int lson=point*2,rson=point*2+1,ly=tree[point].lazy;
		tree[lson].data+=ly*(tree[lson].r-tree[lson].l+1);
		tree[rson].data+=ly*(tree[rson].r-tree[rson].l+1);
		tree[lson].lazy+=ly,tree[rson].lazy+=tree[point].lazy;
		tree[point].lazy=0;
	}
}
void build(int point,int l,int r)
{
	if(point==1)
		tree[point].l=1,tree[point].r=n;
	else if(point%2==1)
	{
		tree[point].l=(tree[point/2].l+tree[point/2].r)/2+1;
		tree[point].r=tree[point/2].r;
	}
	else
	{
		tree[point].l=tree[point/2].l;
		tree[point].r=(tree[point/2].l+tree[point/2].r)/2;
	}
	if(tree[point].l>tree[point].r)return;
	if(tree[point].l==tree[point].r)
	{
		tree[point].data=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(point*2,l,mid);
	build(point*2+1,mid+1,r);
	int lson=point*2,rson=point*2+1;
	tree[point].data=tree[lson].data+tree[rson].data;
}
void change(int point,int l,int r,int x)
{
	if(l<=tree[point].l&&r>=tree[point].r)
	{
		tree[point].lazy+=x;
		tree[point].data+=(tree[point].r-tree[point].l+1)*x;
		return;
	}
	if(l>tree[point].r||r<tree[point].l)
		return;
	lazytag(point);
	int mid=(l+r)/2;
	if(l<=tree[point].l&&tree[point].r<=mid)
		change(point*2,l,r,x);
	else if(mid<=tree[point].l&&tree[point].r<=r)
		change(point*2+1,l,r,x);
	else
	{
		change(point*2,l,r,x);
		change(point*2+1,l,r,x);
	}
	int lson=point*2,rson=point*2+1;
	tree[point].data=tree[lson].data+tree[rson].data;
}
int ask(int point,int l,int r)
{
	if(l<=tree[point].l&&r>=tree[point].r||l==r)
	{
		lazytag(point);
		return tree[point].data;
	}
	if(l>tree[point].r||r<tree[point].l)
		return 0;
	lazytag(point);
	int mid=(l+r)/2;
	if(l<=tree[point].l&&tree[point].r<=mid)
		return ask(point*2,l,r);
	else if(mid<=tree[point].l&&tree[point].r<=r)
		return ask(point*2+1,l,r);
	else return ask(point*2,l,r)+ask(point*2+1,l,r);
}

标签:lazy,point,待补,线段,tree,笔记,int,区间,data
From: https://www.cnblogs.com/WindChime/p/18127502

相关文章

  • 【学习笔记】树链剖分
    一、重链剖分1.定义&作用树链剖分用于解决在树上执行的操作,将树上操作变为区间操作。用区间来维护。2.主要思想&实现有一棵树然后,我们把边区分一下,有一些边是“重边”,其余的是“轻边”,像这样:(红边为重边,黑边为轻)重边和轻边如何确定呢?看每一个结点旁的红色数字,代表他......
  • 【学习笔记】好题
    常来看看。Antiluna好闪,拜谢Antiluna。1.奖金每位参加会谈的代表提出了自己的意见:“我认为员工a的奖金应该比b高!”Mr.Z决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元,且必须是整数。1≤n≤10000,1≤m≤20000。......
  • 【学习笔记】dijkstra
    一、dijkstra1.定义&作用很简单。一个单源最短路算法。2.思想&实现我觉得sm的图已经够了。二、堆优化dijkstra1.先来认识一下proirity_queue2.如何使用proirity_queue对dijkstra优化?每一步,我们都需要找到\(d\)值最小的节点(即\(......
  • 【学习笔记】spfa
    一、spfa模板:voidspfa(intx){ for(inti=1;i<=n;i++) vis[i]=0,dis[i]=inf; dis[1]=0,f[1]=1; q.push(1); while(!q.empty()) { intt=q.front(); q.pop(); vis[t]=0; for(inti=first[t];i;i=e[i].next) { intto=e[i].to; if(dis[t]+e[i].v<......
  • 【学习笔记】并查集
    一、普通并查集1.定义&作用并查集是管理元素分组的算法,可以高效对元素分组(合并为一组)以及查询两个元素是否在一组。2.主要思想&实现对于每一个组,设置一个“组长”,每一次合并时只需要将其中一组的组长设为另一组的组长,查询时只需要查询组长是否相同。每一个组都可以理解......
  • SQL SERVER 从入门到精通 第5版 第二篇 第9章 视图的使用 读书笔记
      第9章视图的使用视图是一种常用的数据库对象,它将查询的结果以虚拟表的形式存储在数据中,视图并不在数据库中以存储数据集的形式存在.视图的结构和内容是建立在对表的查询基础之上的,和表一样包括行和列,这些行,列数据都来源于其所引用的表,并且是在引用视图过程中动......
  • 《C++程序设计》阅读笔记【7-堆和拷贝构造函数】
    ......
  • 机器学习和深度学习--李宏毅(笔记与个人理解)Day9
    Day9LogisticRegression(内涵,熵和交叉熵的详解)中间打了一天的gta5,图书馆闭馆正好+npy不舒服那天+天气不好,哈哈哈哈哈总之各种理由吧,导致昨天没弄起来,今天补更!这里重点注意一下,这个output值是概率哈,也就是说式子整体表示的含义是x属于c1的概率是多大这个老师......
  • python初学者笔记(7)——求和函数总结
    python经常要用到各种求和,例如列表求和,元素求和,利用函数求和,将这些方法总结发给大家!1.python两个数的求和函数defsum_2_num(num1,num2):result=num1+num2returnresult#必须在执行行输入,函数命名后必须调用,调用sum_2_num(),或者print()#sum_2_num(10,20......
  • SQL SERVER 从入门到精通 第5版 第二篇 第8章 SQL数据高级查询 读书笔记
     第8章SQL数据高级查询 >.子查询与嵌套查询>.子查询概述:子查询是一个嵌套在SELECT,INSERT,UPDATE和DELETE语句或者其他子查询中的查询,任何允许使用表达式的地方都可以使用子查询.子查询语法规则如下:>.子查询的SELECT查询总使用圆括号......