poj A Simple Problem with Integers 3468 (线段树区间值更新+优化) 好题

99 篇文章 0 订阅
21 篇文章 0 订阅
A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 80356 Accepted: 24812
Case Time Limit: 2000MS

Description

You have N integers, A1,A2, ... ,AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1,A2, ... ,AN. -1000000000 ≤Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa,Aa+1, ... ,Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa,Aa+1, ... ,Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.
 
//大神的思路。
题意:给定一串数字,进行两种固定操作:一,求某区间内的和;二对某区间内的所以数加上一指定数字。
思路:题目数据量很大,需应用线段树,难点在于对线段树维护过程中的优化。优化通过父亲结点对孩子结点传递数据域的值,同时对父亲结点数据域及时修改。具体方法就是,每次累加一个数字时,没必要把数字一直插入到叶子节点,只要有适合的范围就插入到这个范围中,用一个add记录它,当下一次若有询问时,这个范围若刚好适合询问的范围,就直接把原来这个节点的值加上add乘以范围,再加到result中,就可以了,若这个节点的范围不适合查询的范围的话,就要查询它的子节点了,呢么这时候再把这个add传递给它的子节点,这样在时间上效率就会比较高了。关键是当前结点的sum为当前区间段所有数字的和,add为当前区间中所有数字的待加值,维护的原则是始终保持当前sum和add的正确性。二叉树的整个操作过程均已递归的方式进行,递归过程中只有两种情况,注意思维方式。优化思想与poj2528相似。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100100
using namespace std;
int a[N];
__int64  re;
struct zz
{
	int l;
	int r;
	__int64 m;//m表示区间内的待加值,sum表示当前区间内的和
	__int64 sum; 
}q[4*N];
void build(int gen,int l,int r)
{
	q[gen].l=l;
	q[gen].r=r;
	q[gen].m=0;
	if(l==r)
	{
		q[gen].sum=a[l];
		return ;
	}
	int mid=(q[gen].l+q[gen].r)/2;
	build(gen<<1,l,mid);
	build(gen<<1|1,mid+1,r);
	q[gen].sum=q[gen<<1].sum+q[gen<<1|1].sum;// 回溯赋值
}
void query(int gen,int l,int r)
{
	if(q[gen].l==l&&q[gen].r==r)// 找到整段区间,则累加结果
	{
		re+=(r-l+1)*q[gen].m+q[gen].sum;
		return ;
	}
	// 待查询区段是当前区间的一部分情况,则修改当前区段和,并将m传入两个孩子结点,后清零当前m,递归继续搜索
	q[gen].sum+=(q[gen].r-q[gen].l+1)*q[gen].m;
	q[gen<<1].m+=q[gen].m;
	q[gen<<1|1].m+=q[gen].m;
	q[gen].m=0;
	int mid=(q[gen].l+q[gen].r)/2;
	if(l<=mid)
		query(gen<<1,l,min(r,mid));
	if(r>=mid+1)
		query(gen<<1|1,max(l,mid+1),r);
}
void update(int gen,int l,int r,__int64 m)
{
	if(q[gen].l==l&&q[gen].r==r)// 找到整段区间,则累加m
	{
		q[gen].m+=m;
		return ;
	}
	//维护思路与查询大致相同,仅增加对m的处理
	q[gen].sum+=(q[gen].r-q[gen].l+1)*q[gen].m;// 对当前段的sum累加分两部分
	q[gen].sum+=(r-l+1)*m;
	q[gen<<1].m+=q[gen].m;
	q[gen<<1|1].m+=q[gen].m;
	q[gen].m=0;
	int mid=(q[gen].l+q[gen].r)/2;
	if(l<=mid)
		update(gen<<1,l,min(r,mid),m);
	if(r>=mid+1)
		update(gen<<1|1,max(l,mid+1),r,m);
}
int main()
{
	int n,t;
	int i;
	int x,y;
	__int64 z;
	char c;
	//scanf("%d%d",&n,&t);
	while(scanf("%d%d",&n,&t)!=EOF)
	{
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		build(1,1,n);
		while(t--)
		{
			getchar();
			scanf("%c%d%d",&c,&x,&y);
			if(c=='Q')
			{
				re=0;
				query(1,x,y);
				printf("%I64d\n",re);
			}
			else
			{
				scanf("%I64d",&z);
				update(1,x,y,z);
			}
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值