luoguP1438 无聊的数列(线段树)

这篇博客介绍了一种数据结构——树状数组(也称为线段树),用于高效处理区间更新和查询操作。文章详细阐述了如何维护一个序列,支持在指定范围内加等差数列以及查询特定位置数值的功能。重点讨论了lazy标记的处理策略,包括如何处理多个lazy标记的重叠情况。此外,还提供了一个C++实现的代码示例,展示了树状数组在实际问题中的应用。
摘要由CSDN通过智能技术生成

维护一个序列,支持两种操作:
1.给出一个长度等于r-l+1的等差数列,首项为 k,公差为 d,并将它对应加到 [l,r]范围中的每一个数上。

2.查询第p个数的值。

这道题困难在lazy标记如何处理,首先看出我们需要保存两个值,一个一段区间要加上的等差数列的首项k,另一个是公差d。其次,我们应该发现如果等差数列涉及到两个区间的部分时,我们是不知道较后的那个区间的首项,那么此时我们应该算出该区间的首项,即(前区间的长度*d+k),将其保存。最后当我们遇到多个lazy重叠时,我们不用下放,直接加上去就行。

注意可能会有首项或公差为0的情况,记得加上这种情况。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[100001];
struct node
{
	int l,r,lc,rc,sum;
	int lazy,gc;
};
int len=0;
node e[500000];
int ins(int x,int y)
{
	len++;
	int now=len;
	int l=x,r=y,lc=-1,rc=-1,sum=0;
	if(x==y) sum=a[x];
	else
	{
		int mid=(l+r)/2;
		lc=ins(l,mid);
		rc=ins(mid+1,r);	
	} 
	e[now]={l,r,lc,rc,sum,0,0};
	return now;
}

void down(int now)
{
	int l=e[now].l,r=e[now].r;
	if(l==r) 
	{
		e[now].sum+=e[now].lazy;
		e[now].lazy=0;
		e[now].gc=0;
		return ;
	} 
	else
	{
		int lc=e[now].lc,rc=e[now].rc;
		int mid=(l+r)/2;
		e[lc].lazy+=e[now].lazy;
		e[lc].gc+=e[now].gc;
		e[rc].lazy+=e[now].lazy+e[now].gc*(mid-l+1);
		e[rc].gc+=e[now].gc;
		e[now].lazy=0;
		e[now].gc=0;
		return ; 
	}
}
void cg(int now,int x,int y,int k,int gc)
{
	int l=e[now].l,r=e[now].r;
	if(x==l&&y==r) 
	{
		e[now].lazy+=k;
		e[now].gc+=gc;
		return ;
	}
	else
	{
		if(e[now].lazy!=0||e[now].gc!=0) down(now); 
		int lc=e[now].lc,rc=e[now].rc;
		int mid=(l+r)/2;
		if(x>=mid+1) cg(rc,x,y,k,gc);
		else if(y<=mid) cg(lc,x,y,k,gc);
		else
		{
			cg(lc,x,mid,k,gc);
			cg(rc,mid+1,y,k+gc*(mid-x+1),gc);
		}
	}
}
int find(int now,int x)
{
	int l=e[now].l,r=e[now].r;	
	if(e[now].lazy!=0||e[now].gc!=0) down(now); 
	if(l==x&&r==x) return e[now].sum;
	else 
	{
		int lc=e[now].lc,rc=e[now].rc;
		int mid=(l+r)/2;
		if(x>=mid+1) return find(rc,x);
		else return find(lc,x); 
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int root=ins(1,n);
	for(int i=1;i<=m;i++)
	{
		int p;
		scanf("%d",&p);
		if(p==1)
		{
			int x,y,q,k;
			scanf("%d%d%d%d",&x,&y,&q,&k);
			cg(root,x,y,q,k);
		}
		else
		{
			int x;
			scanf("%d",&x);
//			if(x==261) printf("3250\n");
//			else 
			printf("%d\n",find(root,x));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值