[poj3468]A Simple Problem with Integers_线段树

A Simple Problem with Integers

    题目大意:给出n个数,区间加、查询区间和。

    注释:1<=n,q<=100,000.(q为操作次数)。

      想法:嗯...学了这么长时间线段树,发现我tm竟然不会lazy标记??!好吧,看来线段树又要重新来了。

        关于这道题,主要是联系lazy标记的使用。如果每一次查询的时间复杂度都输满nlogn,那么就gg了。线段树之所以快的原因在于我们在查询到一个可以代替原来查询区间一部分的节点,我们就在这个节点上打一个lazy标记。那么我就不需要继续想该节点之后去遍历。而一个lazy标记表示这个节点的所有子节点都进行了同样的操作。那么如果我在同一个节点处打两个lazy标记,本题是区间加法,我们需要将两个lazy标记相加。我们在需要遍历的时候发现一个节点有lazy标记,我们需要将lazy标记向下推。为什么?

          因为我们打标记是为了节约时间,但是lazy标记下面的节点在当前时刻的值是没有发生更改的,所以若我们需要下面节点的值,则我就必须将上面的lazy标记向下推,就是pushdown。

          我们发现这样的话最上面的lazy标记显然是最新的。紧接着,推下lazy标记之后,我们需要将lazy标记所影响的点的值上传给father,就是pushup。

      如果最后存在一个节点,它仍然有lazy标记但是它始终没有没第二次查询或者修改,我们就节省了将lazy标记作用在它子树里的时间,这就是lazy标记节省时间的根本。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson pos<<1
#define rson pos<<1|1
#define N 100010
using namespace std;
typedef long long ll;
ll lazy[4*N];
int high[N];
ll sum[4*N];
void build(int pos,int l,int r)//建树
{
	int mid=(l+r)>>1;
	if(l==r)
	{
		sum[pos]=high[l];
		return;
	}
	build(lson,l,mid);
	build(rson,mid+1,r);
	sum[pos]=sum[lson]+sum[rson];//pushup
}
inline void pushdown(int pos,int l,int r)
{
	if(lazy[pos])
	{
		int mid=(l+r)>>1;
		sum[lson]+=(mid-l+1)*lazy[pos];//推标记
		lazy[lson]+=lazy[pos];
		sum[rson]+=(r-mid)*lazy[pos];
		lazy[rson]+=lazy[pos];
		lazy[pos]=0;
	}
}
void fix(int pos,int l,int r,int x,int y,ll val)
{
	int mid=(l+r)>>1;
	if(x<=l&&r<=y)
	{
		lazy[pos]+=val;
		sum[pos]+=(r-l+1)*val;
		return;
	}
	pushdown(pos,l,r);
	if(x<=mid)//OTZ ZTY
	{
		fix(lson,l,mid,x,y,val);
	}
	if(y>mid)//OTZ ZTY
	{
		fix(rson,mid+1,r,x,y,val);
	}
	sum[pos]=sum[lson]+sum[rson];//pushup
}
ll query(int pos,int l,int r,int x,int y)
{
	int mid=(l+r)>>1;
	ll temp=0;
	if(x<=l&&r<=y)
	{
		return sum[pos];
	}
	pushdown(pos,l,r);
	if(x<=mid)
	{
		temp+=query(lson,l,mid,x,y);
	}
	if(y>mid)
	{
		temp+=query(rson,mid+1,r,x,y);
	}
	return temp;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&high[i]);
	}
	build(1,1,n);
	char s[10];
	for(int a,b,c,i=1;i<=m;i++)
	{
		scanf("%s",s);
		if(s[0]=='Q')
		{
			scanf("%d%d",&a,&b);
			printf("%lld\n",query(1,1,n,a,b));
		}
		else
		{
			scanf("%d%d%d",&a,&b,&c);
			fix(1,1,n,a,b,c);
		}
	}
	return 0;
}

     小结:重新开始线段树qwq

posted @ 2018-03-26 08:17  JZYshuraK_彧  阅读(165)  评论(0编辑  收藏  举报