线段树(模板)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int x=1e5+5;
int a[x];
//利用线段树完成单点修改和区间查询
//以洛谷P3372为例(典型的线段树单修和区查的例题) 

struct node//用结构体来储存线段数的信息 
{
	int l,r;//m每个点有一个左右区间 
	ll sum,lazy;//代表这个节点的数据总和 
}tree[x*4];

void build(int L,int R,int rt)//分别代表节点的左右区间和节点值 
{
	//通过递归进行建树
	if(L==R)
	{
		tree[rt].sum=a[L];
		tree[rt].l=tree[rt].r=L;
		//当到达最后的子节点的时候,每个子节点的特点是左右区间相同 
		return;
	} 
	int mid=(L+R)>>1;
	build(L,mid,rt*2);//递归走左分支 
	build(mid+1,R,2*rt+1);//递归走右分支 
	
	//每次递归依次走完一个子节点重新更新一下父节点的信息 
	tree[rt].sum=tree[rt*2].sum+tree[rt*2+1].sum;
	tree[rt].l=L;
	tree[rt].r=R; 
} 

//重新给一个标志进行赋值
void pushdown(int L,int R,int rt,ll add)
{
	//作用:更新该点的子节点的lazy的值,并还原该点的lazy的值,使得lazy值呈现一个向下移动的样子 
	int mid=(L+R)>>1;
	tree[rt*2].sum+=(mid-L+1)*add;
	tree[rt*2+1].sum+=(R-mid)*add;
	tree[rt*2].lazy+=add;
	tree[rt*2+1].lazy+=add;
	tree[rt].lazy=0;
	return;	
} 

//同样通过递归的方式进行单点修改 
void update(int L,int R,int rt,int l,int r,ll add)
{//参数分别为查询区间,指定节点,指定的左右区间端点值,进行修改的变化值 
	if(R<l||L>r)
	{
		return;//当所查询的区间与指定区间无交集的时候结束函数 
	}	
	if(L>=l&&R<=r)//当前结点是修改区间的子区间 
	{
		tree[rt].lazy+=add;
		tree[rt].sum+=(R-L+1)*add;//重新给有标记的点进行赋值 
		return;
	}
	if(tree[rt].lazy)//当当前结点的lazy为非0是 
	{
		pushdown(L,R,rt,tree[rt].lazy);//将该节点的子节点的lazy附上值,将当前结点的值修改为0
		//当上一个if判断不符合条件是,lazy值向下走(即该点的子节点继承该点的lazy值,该点的lazy值改为0 
	}
	int mid=(R+L)>>1;
	update(L,mid,rt*2,l,r,add);//更新左分支
	update(mid+1,R,rt*2+1,l,r,add);//更新右分支 
	tree[rt].sum=tree[rt*2].sum+tree[rt*2+1].sum;//重新将给其父节点进行赋值 
} 

ll query(int L,int R,int rt,int l,int r)
{
	if(R<l||L>r)
	{
		return 0;
	}
	if(L>=l&&R<=r)
	{
		return tree[rt].sum;
	}
	if(tree[rt].lazy)
	{
		pushdown(L,R,rt,tree[rt].lazy);//作用和修改的相同,使lazy值移动 
	}
	int mid=(L+R)>>1;
	return query(L,mid,rt*2,l,r)+query(mid+1,R,rt*2+1,l,r);
}

int main()
{
 	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}	
	build(1,n,1);//用build函数进行建树,线段树 
//	for(int i=1;i<=n;i++)
//	{
//		printf("[%d,%d]==%d\n",tree[i].l,tree[i].r,tree[i].sum);//打印线段树 
//	} 
	while(m--)
	{
		int b,c,d;
		ll k;
		cin>>b>>c>>d;
		//两种操作均需使用函数来完成 
		if(b==1)//单点修改(和区间修改差不多) 
		{
			cin>>k;//修改加上的值
			update(1,n,1,c,d,k); //传的参数有点儿多 
		}
		else//区间查询 
		{
			cout<<query(1,n,1,c,d)<<endl;
		}
	}
	
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值