线段树的区间修改操作poj3468

题意:给一个数组,进行区间修改和求和操作。

线段树区间修改,直接放线段树咬,但是很多的细节需要注意。

千万不能upate(L,mid,2*k,val);update(mid+1,R,2*k+1,val).会改变目标区间的值。

比如n=10,修改区间(3,6)

1,左边:目标区间变为(3,5),树的区间为(1,5)

    右边:目标区间变为(6,6)树的区间变为(6,10)

2, 左边:①左边:目标区间(3,3)树的区间(1,3)②右边:目标区间(4,5)树的区间(4,5)

(到目前为止都很正常,接下来右边的就不对了)

右边:①左边:目标区间(6,8)(这个时候就不对了,因为原来的目标区间是(3,6)而现在到了8了)。树的区间(6,8)

②右边:没有

#pragma warning(disable:4996)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
struct node
{
	ll sum, lz, l, r;
};
node tree[MAXN<<2];
//建立线段树,没有坑,没有细节
void build(ll L, ll R, ll k)
{
	tree[k].l = L;tree[k].r = R;
	if (L == R)
	{
		scanf("%lld", &tree[k].sum);
		tree[k].lz = 0;
		return;
	}
	ll mid = (L + R) / 2;
	build(L, mid, 2 * k);
	build(mid + 1, R, 2 * k + 1);
	tree[k].sum = tree[2 * k].sum + tree[2 * k + 1].sum;
}
//要加上父亲结点的lazy标记,左右区间的元素个数要数对
void pushdown(ll k)
{
	if (tree[k].lz)
	{
		tree[2*k].lz+=tree[k].lz;
		tree[2 * k + 1].lz += tree[k].lz;
		ll mid = (tree[k].l + tree[k].r) / 2;
		//左区间元素个数mid-tree[2*k].l+1
		tree[2 * k].sum += (mid - tree[2 * k].l + 1) * tree[k].lz;
		//右区间tree[2*k+1].r-mid
		tree[2 * k + 1].sum += (tree[2 * k + 1].r - mid) * tree[k].lz;
		tree[k].lz = 0;
	}
}
void update(ll L, ll R, ll k, ll val)
{
	if (tree[k].l >= L && tree[k].r <= R)
	{
		tree[k].sum += (tree[k].r - tree[k].l + 1) * val;
		tree[k].lz += val;
		return;
	}
	pushdown(k);
	ll mid = (tree[k].l + tree[k].r) / 2; 
	//不能是upate(L,mid,2*k,val);update(mid+1,R,2*k+1,val)会出错
	//看起来好像差不多,但是这样做,会使(L,R)改变
	if (L <= mid)update(L, R, 2 * k, val);
	if(R>mid) update(L, R, 2 * k + 1, val);
	tree[k].sum = tree[2 * k].sum + tree[2 * k + 1].sum;
}
ll query(ll L, ll R, ll k)
{
	if (tree[k].l >= L && tree[k].r <= R)
	{
		return tree[k].sum;
	}
	//记得pushdown,不然会少算很多
	pushdown(k);
	ll ans = 0, mid;
	mid = (tree[k].l + tree[k].r) / 2;
	if (L <= mid)ans += query(L, R, 2 * k);
	if (R > mid)ans += query(L, R, 2 * k + 1);
	return ans;
}
//debug的函数,查看每一个数字的值
/*void find(int k)
{
	if (tree[k].l == tree[k].r)
	{
		printf("%lld ", tree[k].sum);
		return;
	}
	pushdown(k);
	int mid = (tree[k].l + tree[k].r) / 2;
	find(2 * k);
	find(2 * k + 1);
	//printf("\n");
}*/
int main()
{
	ll n, m, i, j;
	char s[2];
	while (scanf("%lld%lld", &n, &m) == 2)
	{
		build(1, n, 1);
		ll t1, t2, t3,ans;
		while (m--)
		{
			scanf("%s", s);
			if (s[0] == 'Q')	
			{
				scanf("%lld%lld", &t1, &t2);
				ans = query(t1, t2, 1);
				printf("%lld\n", ans);
			}
			else
			{
				scanf("%lld%lld%lld", &t1, &t2, &t3);
				update(t1, t2, 1, t3);
			}
		}
		//find(1);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值