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;
}