A Simple Problem with Integers -POJ 3468

版权声明:Power by PowderHan https://blog.csdn.net/weixin_42827051/article/details/83545977

A Simple Problem with Integers

									**POJ 3468**

题目大意
有n个数A1 A2 A3 … An,q个操作。N and Q. 1 ≤ N,Q ≤ 100000.
每个询问支持两种操作
C a b c 将Aa 到 Ab都加上c
Q a b 求Aa…Ab的区间和
-1000000000 ≤ Ai ≤ 1000000000.
-10000 ≤ c ≤ 10000.

题目分析
非常简单的一道线段树区间修改模板题。
(树状数组也可做只不过更麻烦)
主要是用于入门和练手。
我们用一棵线段树来维护区间和,对于区间修改的操作,如果我们朴素地使用修改的方式逐一对包含的结点进行更新,那么最差的情况下可能会导致所有的结点都发生改变而更新,这样我们修改一次的复杂度就退化到了O(n),显然是无法接受的。
试想,如果我们在一次修改指令的过程中发现结点p代表的区间[pl,pr]被修改区间[l,r]完全覆盖,并且逐一更新了子树p中所有的结点,但是可能在之后的查询中却根本不需要用到[pl,pr]的子区间作为候选答案,那么更新p的所有子树就是徒劳的。
换句话说,如果当前结点p被完全包含在了[l,r]中,那么我们可以立即返回,不过我们在返回之前需要在p上留下一个标记,表示该节点曾经被修改过,但它的子节点暂时还未更改

这就是常见的懒惰标记思想。

如果在后续的操作中,我们需要访问这个结点p的子节点,也就是需要从p向下递归,那么我们可以检查p上是否含有这个懒惰标记,如果有标记,那么我们就根据标记信息更新p的两个子节点,同时给两个子节点打上标记,再将p的标记清除。这样就让每次的递归都是用在了有用的结点上。

换句话说,除了在对于这个区间的操作分成的O(logn)个线段的结点进行直接修改外,
对别的任意的节点的修改都延迟到**在后续操作中递归进入到它的父节点并且需要继续向下递归时**再进行。

这样一来,每次查询或者修改区间的操作复杂度都是O(nlogn)。

延迟标记提供了线段树中从上向下传递信息的方法。

同时需要注意的时,一个节点被打上“懒惰标记”的同时,它自身的保存信息已经被修改完毕。
同时传递完标记并且递归完,应该重新计算维护当前节点的信息。

同时数据较大应该使用long long

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

#define LL long long
const int MAXN=100005;
struct Node
{
	LL sum,addv;
}tree[MAXN<<2];
int n,m;

void build_tree(int o,int l,int r)
{
	if(l==r)
	{
		int x;	scanf("%d",&x);
		tree[o].sum=x;
		return;
	}
	int mid=(l+r)>>1;
	build_tree(o<<1,l,mid);
	build_tree(o<<1|1,mid+1,r);
	tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}

void push_down(int o,int l,int r)
{
	if(!tree[o].addv)
		return;
	int lc=(o<<1),rc=(o<<1|1);
	int mid=(l+r)>>1;
	tree[lc].addv+=tree[o].addv;
	tree[rc].addv+=tree[o].addv;
	tree[lc].sum+=tree[o].addv*(mid-l+1);
	tree[rc].sum+=tree[o].addv*(r-mid);
	tree[o].addv=0;
}

LL query(int o,int l,int r,int x,int y)
{
	if(x<=l&&y>=r)
		return tree[o].sum;
	push_down(o,l,r);
	LL ans=0;
	int mid=(l+r)>>1;
	if(x<=mid)
		ans+=query(o<<1,l,mid,x,y);
	if(y>mid)
		ans+=query(o<<1|1,mid+1,r,x,y);
	return ans;
}

void change(int o,int l,int r,int x,int y,int p)
{
	if(x<=l&&y>=r)
	{
		tree[o].sum+=(LL)(r-l+1)*p;
		tree[o].addv+=p;
		return;
	}
	push_down(o,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)
		change(o<<1,l,mid,x,y,p);
	if(y>mid)
		change(o<<1|1,mid+1,r,x,y,p);
	tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}

void init()
{
	cin>>n>>m;
	build_tree(1,1,n);
}

void work()
{
	char k;
	int x,y,z;
	while(m--)
	{
		cin>>k;
		if(k=='Q')
		{
			scanf("%d%d",&x,&y);
			printf("%lld\n",query(1,1,n,x,y));
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			change(1,1,n,x,y,z);
		}
	}
}

int main()
{
	init();
	work();
	return 0;
}
     

猜你喜欢

转载自blog.csdn.net/weixin_42827051/article/details/83545977