POJ 3468 A Simple Problem with Integers 详解(线段树)

第一次写线段树,感觉还是可以理解的微笑(借鉴了coder_hsc的写法)

原文地址:http://blog.csdn.net/w00w12l/article/details/7920846

题目链接:http://poj.org/problem?id=3468

线段树为什么要开4倍空间:http://scinart.github.io/acm/2014/03/19/acm-segment-tree-space-analysis/

                                                  http://bbs.csdn.net/topics/380264561

题目大意:两种操作1.更新一段区间的值,即将一段区间上每个点都加上一个值。2.查询一段区间的和。

因操作数过大,故直接暴力是不行的。

建树操作:

    lef,rig左右边界,sum和mark乘以区间长度,是lef到rig区间上的和,初始mark为0,递归建树,到叶子节点停止递归。

更新操作:

    如果更新区间恰好相同,直接将更新值加到懒标记上,若不完全相同(因为查询至上而下,故区间必定包含更新区间),直接在该节点sum值上加上更新区间长度乘以更新的值。并根据更新区间与该区间的位置,递归继续更新,直至完全相符。

查询操作:

    如果区间完全相符,返回sum和mark乘以区间长度之和 。若不完全相符,将懒标记下放,并在该区间sum值上加上懒标记乘以区间长度,并将mark值置0。并继续根据位置递归查询。

实现代码如下:

#include <iostream>
#include <stdio.h>
#define maxn 100010
using namespace std;
struct node
{
 	int lef,rig,mid;   //该节点的左右边界,中值 
 	long long int sum,mark;//sum,mark乘区间之和为该区间总值,但sum不一定就是原值,会被更新 
}Tree[maxn<<2];//一定要移两位,是有理论依据的,见上面链接,开3倍可能只是测试数据不够大,所以一直没事
int store[maxn];//存原序列 ,下标从1开始 
void build_tree(int id,int lef,int rig)
{
	int mid=(lef+rig)>>1;
	//初始化 
	Tree[id].lef=lef;
    Tree[id].rig=rig;
    Tree[id].mid=mid;
    Tree[id].mark=0;
   //到了叶子 
	if(lef==rig)
	{
 	   Tree[id].sum=store[lef];//lef刚好和原序列下标对应 
 	   return;
    }
      build_tree(id<<1,lef,mid);
      build_tree((id<<1)|1,mid+1,rig);
   //该节点左右树已建成 
   Tree[id].sum=(Tree[id<<1].sum+Tree[(id<<1)|1].sum);
}
void update(int id,int lef,int rig,int val)
{
	//如果区间完全相符,直接更新mark
	if(Tree[id].lef==lef&&Tree[id].rig==rig)
	{
      Tree[id].mark+=val;
      return; 	
     } 
   //若不完全相符,查询区间必定包含在该区间内,故直接更新该点sum
   //因为更新的区间在该区间内,所以sum值可以直接更新 
    Tree[id].sum+=(long long)val*(rig-lef+1); 
   //完全落在mid左侧
    if(Tree[id].mid>=rig)
    update(id<<1,lef,rig,val);
   //完全落在mid右侧 
    else if(Tree[id].mid<lef)
     update((id<<1)|1,lef,rig,val);
   //落在两侧 ,两侧都更新 
    else
   {
      update(id<<1,lef,Tree[id].mid,val); 
      update((id<<1)|1,Tree[id].mid+1,rig,val);
   }
}
long long int query(int id,int lef,int rig)
{
	//如果区间完全相符,返回sum和mark之和 
   if(Tree[id].lef==lef&&Tree[id].rig==rig)  
    return Tree[id].sum+Tree[id].mark*(long long)(rig-lef+1);
   //区间不完全相符,但查询区间一定是从大到小,故需下放懒标记 
    if(Tree[id].mark!=0)
    {
    	//将mark值放入sum中,mark清空,避免二次赋值mark给子树
    	//该节点区间大小大于查询区间大小 
		
		Tree[id<<1].mark+=Tree[id].mark;
        Tree[(id<<1)|1].mark+=Tree[id].mark;
		Tree[id].sum+=(long long)(Tree[id].rig-Tree[id].lef+1)*Tree[id].mark;
        Tree[id].mark=0;
    }
    //完全在左侧 
    if(Tree[id].mid>=rig)
          return query(id<<1,lef,rig);
    //在右侧 
    else if(Tree[id].mid<lef)
          return query((id<<1)|1,lef,rig);
    //返回两边之和 
    else
          return query(id<<1,lef,Tree[id].mid)+query((id<<1)|1,Tree[id].mid+1,rig);
}
int main()
{
	int n,m,l,r,v;
	char c;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&store[i]);
		}
		build_tree(1,1,n);
		for(int i=0;i<m;i++)
		{
			getchar();
			scanf("%c",&c);
		    if(c=='Q')
			{  
                 scanf("%d%d",&l,&r);  
                 printf("%lld\n",query(1,l,r));  
            }  
            else
			{  
                  scanf("%d%d%d",&l,&r,&v);  
                  update(1,l,r,v);  
			}
		}
	}
}
 





你可能感兴趣的:(线段树,poj)