线段树

基本概念

   线段树的每个节点表示一个区间,主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

  1. 每个节点的左孩子区间范围为[ l,mid ],右孩子为[ mid+1 , r ]
  2. 对于结点k,左孩子结点为2 * k,右孩子为2*k+1。

在这里插入图片描述

基本操作

struct node
{
    int l,r; //l,r分别表示区间左右端点
    int w; // w为你想要维护的东西,这里假装维护的是区间和
    int add; // 懒标记(后面会解释)
}t[4*n+1];
状态回溯

这里维护区间和

void push_up(int rt)
{
	t[rt].w=t[rt<<1].w+t[rt<<1|1].w;
}
建树
  1. 二分每个结点,给他左右端点的范围
  2. 找到叶子结点,赋值
  3. 状态合并
void build(int l,int r,int rt)
{
	t[rt].l=l;
	t[rt].r=r;
	if(l==r)  //叶子节点
	{
		scanf("%d",&t[rt].w); // 赋值 
		return ;
	}
	int m=(l+r)/2;
	build(l,m,rt<<1);//递归左子树 
	build(m+1,r,rt<<1|1);//递归右子树 
	push_up(rt); //状态合并(回溯)
}
单点修改
  1. 二分寻找需要的点
  2. 修改后,要回溯修改他的祖先节点们
void updata_single(int rt,int a,int b)
/* rt为当前节点 a为待修改节点的编号,b为修改的值*/
{ 
	if(t[rt].l==t[rt].r) // 找到需要修改的节点 
	{
		t[i].w=b;
		return ;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(a<=mid) updata_single(rt<<1,a,b); //递归左子树
	else updata_single(rt<<1|1,a,b);  //递归右子树
	push_up(rt);  // 状态回溯
}
单点查询

方法同上面差不多

int quary_single(int rt,int a)
/* rt为当前节点 a为需要节点的编号*/
{ 
	if(t[rt].l==t[rt].r) // 找到需要修改的节点 
	{
		return t[i].w;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(a<=mid) quary_single(rt<<1,a); //递归左子树
	else quary_single(rt<<1|1,a);  //递归右子树
}
区间修改

我们引入一个懒标记

  1. 存储到这个节点的修改信息,暂时不把修改信息传到子节点。
  2. 实现思路:
    a.递归到这个节点时,只更新这个节点的状态,并把当前的更改值累积到标记中。
    b.当需要递归这个节点的子节点时,将标记下传给子节点。
    c.下传操作:
       ①当前节点的懒标记累积到子节点的懒标记中。
       ②修改子节点状态。在这里,就是原状态+子节点区间点的个数*父节点传下来的懒标记。
       ③父节点懒标记清0。这个懒标记已经传下去了,不清0后面再用这个懒标记时会重复下传。

当前节点的标志域向孩子节点传递

void push_down(int rt)
{
	if(t[rt].add==0) return;
	// 向孩子节点传递懒标记 
	tree[rt<<1].add+=tree[rt].add;
	tree[rt<<1|1].add+=tree[rt].add;
	//修改子节点状态
	tree[rt<<1].w+=tree[rt].add*(tree[rt<<1].r-tree[rt<<1].l+1);
	tree[rt<<1|1].w+=tree[rt].add*(tree[rt<<1|1].r-tree[rt<<1|1].l+1);
	//父节点懒标记清0
	tree[rt].add=0;
}

这里表示在区间[ x , y ]中的每一个数加上w

void update_interval(int rt, int l, int r, int x, int y, int w)
{
	if(x>r||y<l) return ; //待修改区间和当前节点区间没有交集
	if(x<=l&&r<=y) //当前节点区间包含在待修改区间内
	{
		t[rt].add+=w;
		t[rt].w+=w;
		return ;
	}
	push_down(rt); //延迟标记向下传递
	int mid=(l+r)>>1;
	//更新左右孩子节点
	update_interval(rt>>1,l,mid,x,y,w);
	update_interval(rt>>1|1,mid+1,r,x,y,w);
	push_up(rt);
}
区间查询
int query_interval(int rt,int l,int r,int x,int y)
{
	if(x>r||y<l) return 0; //查询区间和当前节点区间没有交集
	if(x<=l&&r<=y) return t[rt].w; //当前节点区间包含在查询区间内
	push_down(rt); //延迟标志域向下传递
	int mid=(l+r)>>1;
	return query_interval(rt<<1,l,mid,x,y)+query_interval(rt<<1|1,mid+1,r,x,y);
}

总和

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int l,r,w,add;
} t[4000010];

void push_up(int rt)
{
	t[rt].w=t[rt<<1].w+t[rt<<1|1].w;
}
void build(int l,int r,int rt)
{
	t[rt].l=l;
	t[rt].r=r;
	if(l==r)
	{
		scanf("%d",&t[rt].w);
		return ;
	}
	int m=(l+r)/2;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	push_up(rt);
}

void push_down(int rt)
{
	if(t[rt].add==0) return;
	tree[rt>>1].add+=tree[rt].add;
	tree[rt>>1|1].add+=tree[rt].add;
	tree[rt>>1].w+=tree[rt].add*(tree[rt>>1].r-tree[rt>>1].l+1);
	tree[rt>>1|1].w+=tree[rt].add*(tree[rt>>1|1].r-tree[rt>>1|1].l+1);
	tree[k].add=0;
}
int quary_single(int rt,int a)
{
	if(t[rt].l==t[rt].r)
	{
		return t[i].w;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(a<=mid) quary_single(rt<<1,a);
	else quary_single(rt<<1|1,a);
}
void updata_single(int rt,int a,int b)
{
	if(t[rt].l==t[rt].r)
	{
		t[i].w=b;
		return ;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(a<=mid) updata_single(rt<<1,a,b);
	else updata_single(rt<<1|1,a,b);
	push_up(rt);
}
int query_interval(int rt,int l,int r,int x,int y)
{
	if(x>r||y<l) return 0;
	if(x<=l&&r<=y) return t[rt].w;
	push_down(rt);
	int mid=(l+r)>>1;
	return query_interval(rt<<1,l,mid,x,y)+query_interval(rt<<1|1,mid+1,r,x,y);
}
void update_interval(int rt, int l, int r, int x, int y, int w)
{
	if(x>r||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[rt].add+=w;
		t[rt].w+=w;
		return ;
	}
	push_down(rt);
	int mid=(l+r)>>1;
	update_interval(rt>>1,l,mid,x,y,w);
	update_interval(rt>>1|1,mid+1,r,x,y,w);
	push_up(rt);
}
int main()
{
	int n,m,op,l,r,x,y;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while(m--)
	{
		scanf("%d",&op);
		ans=0;
		if(op==1)//单点查询,输出第x个数
		{
			scanf("%d",&x);
			printf("%d",quary_single(1,x));
		}
		else if(op==2) //单点修改
		{
			scanf("%d%d",&x,&y);
			updata_single(1,x,y);
		}
		else if(op==3)//区间查询
		{
			scanf("%d%d",&l,&r);
			printf("%d\n",query_interval(1,n,l,r));
		}
		else
		{
			scanf("%d%d%d",&l,&r,&x);//区间修改
			update_interval(1,1,n,l,r,x);
		}
	}
}
题目

上洛谷去找吧~~ (〃‘▽’〃)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值