poj 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)

http://poj.org/problem?id=3468


题意:有n个数和两种操作,"C A B C"和" Q AB",“C A B C”表示区间A~B的数均增加C," Q AB"表示询问区间A~B的和。


思路:

是典型的线段树成段更新和区间求和问题。不过我被书上误导了,导致调试了一下午,最后搞得特晕。所以,还是要相信自己的想法,不要完全依赖别人的。。。

区间线段更新时,有向上更新(父节点)和向下更新(子节点)。一定要理解好它。当对某一区间更新时,比如更新

[1,3],同时它的父节点[1,5]也要更新。向下更新即当前区间不能被要更新区间完全覆盖,那么就要把当前区间的公共增量向左右子树传递,并把当前区间的增量改为0,同时左右子树的sum值也要改变,add的改变和sum的改变的同步的。


#include <stdio.h>
#include <string.h>
#define LL long long

const int maxn = 100005;
__int64 a[maxn];

struct line
{
	int l;
	int r;
	LL sum,add;//增加两个区域,一个记录区间之和,一个记录区间公共增量
}tree[maxn<<2];

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].add = 0;
	if(l == r)
	{
		tree[v].sum = a[r];
		return;
	}

	int mid = (l+r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
	tree[v].sum = tree[v*2].sum + tree[v*2+1].sum;

}

void update(int v, int l, int r,__int64 add)	//更新区间l~r分别加上add;
{
	tree[v].sum += (r-l+1)*add;//不论该区间是否是要更新区间,肯定是当前区间的子区间,所以也要更新当前区间sum值。
	
	if(tree[v].l == l && tree[v].r == r)//如果当前区间恰好被要更新区间完全覆盖,那么用add维护当前区间公共增量,不必往下更新了。
	{
		tree[v].add += add;		//注意是 +=,因为节点本身也有增量
		return;
	}

	int mid = (tree[v].l + tree[v].r)>>1;

	if(tree[v].add)	//如果上面没有走掉,就把增量向下传递,注意传递增量的同时左右子树的sum值也要改变
	{
		tree[v*2].add += tree[v].add;
		tree[v*2+1].add += tree[v].add;
		tree[v*2].sum += tree[v].add * (tree[v*2].r-tree[v*2].l+1);
		tree[v*2+1].sum += tree[v].add * (tree[v*2+1].r-tree[v*2+1].l+1);
		tree[v].add = 0;
	}

	if(r <= mid)
		update(v*2,l,r,add);
	else
	{
		if(l > mid)
			update(v*2+1,l,r,add);
		else
		{
			update(v*2,l,mid,add);
			update(v*2+1,mid+1,r,add);
		}
	}
	
}

__int64 query(int v, int l, int r)
{
	if(tree[v].l == l && tree[v].r == r)
		return tree[v].sum;

	int mid = (tree[v].l + tree[v].r)>>1;

	if(tree[v].add)//上面没走掉,同样增量向下传递,子树的sum值也在改变。
	{
		tree[v*2].add += tree[v].add;
		tree[v*2+1].add += tree[v].add;
		tree[v*2].sum += tree[v].add * (tree[v*2].r-tree[v*2].l+1);
		tree[v*2+1].sum += tree[v].add * (tree[v*2+1].r-tree[v*2+1].l+1);
		tree[v].add = 0;
	}

	if(r <= mid)
		query(v*2,l,r);
	else
	{
		if(l > mid)
			return query(v*2+1,l,r);
		else
			return query(v*2,l,mid)+ query(v*2+1,mid+1,r);
	}
}

int main()
{
	int n,m;
	char ch[2];
	int l,r;
	__int64 ans,add;
	scanf("%d %d",&n,&m);
	for(int i = 1; i <= n; i++)
		scanf("%I64d",&a[i]);

	build(1,1,n);

	while(m--)
	{
		scanf("%s",ch);
		if(ch[0] == 'C')
		{
			scanf("%d %d %I64d",&l,&r,&add);
			update(1,l,r,add);
		}

		else
		{
			scanf("%d %d",&l,&r);
			ans = query(1,l,r);
			printf("%I64d\n",ans);
		}
	}
	return 0;
}



你可能感兴趣的:(线段树,成段更新)