POJ3468:A Simple Problem with Integers

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

           线段树的第二题,这题是关于区间元素的插入以及求和,用线段树在适合不过了。遵循线段树的解题一般情况,先创建线段树,然后不断的插入元素,然后根据题目要求,对元素进行修改以及查询。这里主要说一说修改操作和查询操作。这两个操作都采用Lazy办法,也就是在修改或者查询的时候,对于查询或者修改区间相同的时候,不必急于修改其子节点的值,而是通过设置一位标志位,标志位延迟处理,等到需要对其子节点进行查询或者修改的时候才对其子节点以及其本身的值进行更新修改操作。

         结合本题来说,在增加时,如果要加的区间节点正好覆盖一个节点时,则增加节点的nCarry(增量)值,不再往下继续走,否则要更新节点的nSum值,并且在将增量值向下传,即要修改子节点的值。在查询的时候如果待查区间不是正好覆盖一个节点就将节点的增量值向下传,修改子节点的值,并且要修改对应的父节点的nSum值,并且需要将父节点的增量值赋为0,接下来再继续向下查询。这样就提高 了访问的效率。

代码如下:

#include<iostream>
#include<stdio.h>

using namespace std ;

struct Node{
	int left ;
	int right ;
	long long nSum   ;//该区间的和 
	long long nCarry ;//进位 
}node[400000] ;

void build(int left , int right , int step) ;
void insert(int pos , int val , int step)   ;
void query(int left , int right , int step) ;
void Add_node(int left , int right , long long ncarry , int step) ;


int n ;
int m ;
long long carry ;
long long nSum  ;

int main()
{
	int i ;
	int p ;
	int q ; 
	char c ;
	
	scanf("%d %d" , &n , &m) ;
	//构建线段树 
	build(1 , n , 0) ;
	
	i = 1 ;
	while(i <= n)
	{
		scanf("%d" , &p) ;
		
		insert(i , p , 0) ;
		i ++ ;
	}
	
	while(m--)
	{
		getchar() ;
		
		scanf("%c %d %d" , &c , &p , &q) ;
		
		if(c=='C')
		{
			scanf("%lld" ,&carry) ;
			Add_node(p , q , carry , 0) ;
		}
		else{
			nSum = 0 ;
			
			query(p  , q  , 0) ;
			
			printf("%lld\n" , nSum) ;
		}
	}
	
	return 0 ;
}

void build(int left , int right , int step)
{
	node[step].left = left ;
	node[step].right = right ;
	node[step].nSum = 0 ;
	node[step].nCarry = 0 ;
	
	int mid = (left + right)/2 ;
	int pl = step * 2 + 1 ;
	int pr = step * 2 + 2 ;
	
	if(left != right)
	{
		build(left , mid , pl) ;
		build(mid + 1 , right , pr) ;
	}
}

void insert(int pos , int val , int step)
{
	if(node[step].left == pos && node[step].right == pos)
	{
		node[step].nSum = val ;
		return ;
	}
	
	node[step].nSum += val ;
	
	int mid = (node[step].left + node[step].right) / 2 ;
	
	int pl = step * 2 + 1 ;
	int pr = step * 2 + 2 ;
	 
	if(mid < pos)
		insert(pos , val , pr) ;
	else
		insert(pos , val , pl) ;	
}

void Add_node(int left , int right , long long ncarry , int step)
{
	//如果两个区间刚好相同,则只更新对应的增加值,而不更改nSum值 
	if(node[step].left == left && node[step].right == right)
	{
		node[step].nCarry += ncarry ;
		return ;
	}
	//否则更新对应的增量值,并且将增量向下传 
	
	node[step].nSum += ncarry * (right - left + 1) ;
	
	int mid = (node[step].left + node[step].right) / 2 ;
	int pl = step * 2 + 1 ;
	int pr = step * 2 + 2 ;
	
	if(mid < left)
		Add_node(left , right , ncarry ,pr) ;
	else if(mid >= right)
		Add_node(left , right , ncarry ,pl) ;
	else
	{
		Add_node(left , mid , ncarry , pl) ;
		Add_node(mid + 1,  right , ncarry , pr) ;
	}		
}

void query(int left , int right , int step)
{
	if(node[step].left == left && node[step].right == right)
	{
		nSum += node[step].nSum + node[step].nCarry * (right - left + 1) ;
		return  ;
	}
	int mid = (node[step].left + node[step].right ) /2 ;
	int pr = step * 2 + 2 ;
	int pl = step * 2 + 1 ;
	
	node[step].nSum += node[step].nCarry * (node[step].right - node[step].left + 1) ;
	
	Add_node(node[step].left , mid , node[step].nCarry , pl ) ;
	Add_node(mid + 1 , node[step].right , node[step].nCarry , pr) ;
	
	node[step].nCarry = 0 ;
	
	if(right <= mid)
		query(left , right , pl) ;
	else if(mid < left)
		query(left , right , pr) ;
	else{
		query(left , mid , pl) ;
		query(mid + 1 , right , pr) ;
	}		
		
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值