您的位置:首页 > 其它

POJ_3468 A Simple Problem with Integers(线段树+lazy标记)

2015-08-24 17:32 375 查看
题目请点我

题解:

这是一道简单的线段树外加lazy标记,与之前一道HDU_1698 Just a Hook的区别在于这题区间内的数值不完全相同,lazy数组和segTree数组将不能共用。

这道题让我对lazy数组又进行了思考,lazy数组作为一个辅助数组,它的作用就是用来统一描述某一个节点发散出去的子树的性质。借用一个形象的比喻,是爸爸懒得去更新所有的孩子而做的一个标记,从该爸爸节点发散出去的所有孩子所具有的共同特点。而segTree数组与线段树的性质相对应,必须真实的反映该字区间的状态,两者的关系需要理清,并且始终保持对应。当爸爸的一部分孩子需要改变时,他们将不具有相同的性质,爸爸就要利用这个标记去先更新好所有儿子,然后再另它的部分儿子去改变性质,而此时因为儿子不具有共同性质,爸爸的lazy标志也就不存在了。

void pushdown(int root,int l,int r){
//把性质传给儿子
lazy[LCHILD(root)] += lazy[root];
lazy[RCHILD(root)] += lazy[root];
int mid = MID(l,r);
//确保儿子segTree真实性
segTree[LCHILD(root)] += lazy[root]*(mid-l+1);
segTree[RCHILD(root)] += lazy[root]*(r-mid);
//不再具有标记功能
lazy[root] = 0;
return ;
}


只要能更很好的保证两者的一致性,维护线段树也就很简单了。但注意这道题不是赋值,而是添加,所以我们都是在原来的基础上+=,每次更新后保证segTree是当前字区间的真实状态。

数据上要用LL,否则会超的。。。

plus:其实用结构体数组设计线段树也是很好的,每个节点的性质可以随意定义。只不过现在习惯了这种方式,等以后需要了再去修改吧,原理是不变的!

代码实现:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#define MAX 100010
#define LL long long
#define LCHILD(x) x<<1
#define RCHILD(x) x<<1|1
#define MID(x,y) (x+y)>>1

using namespace std;

int N,Q;
char ope;
LL num[MAX];
LL lazy[MAX<<2];
LL segTree[MAX<<2];
void pushup(int root);
void pushdown(int root,int l,int r);
void build(int root,int l,int r);
LL query(int a,int b,int l,int r,int root);
void update(int a,int b,int l,int r,int root,int add);
int main()
{
while( scanf("%d%d",&N,&Q) != EOF ){
for( int i = 1; i <= N; i++ ){
scanf("%I64d",&num[i]);
}
build(1,1,N);
while( Q-- ){
getchar();
scanf("%c",&ope);
int a,b,c;
if( ope == 'Q' ){
scanf("%d%d",&a,&b);
printf("%I64d\n",query(a,b,1,N,1));
}
else if( ope == 'C' ){
scanf("%d%d%d",&a,&b,&c);
update(a,b,1,N,1,c);
}
}
}
return 0;
}

void build(int root,int l,int r){
lazy[root] = 0;
if( l == r ){
segTree[root] = num[l];
return ;
}
int mid =MID(l,r);
build(LCHILD(root),l,mid);
build(RCHILD(root),mid+1,r);
pushup(root);
return ;
}

void update(int a,int b,int l,int r,int root,int add){
if( a > r || b < l ){
return ;
}
//注意这里lazy+=add,而segTree亦是仅在原基础上+了add部分
//之前写成+lazy部分,WA惨了
if( a <= l && r <= b ){
lazy[root] += add;
segTree[root] += (r-l+1)*add;
return;
}
//注意先更新儿子
if( lazy[root] != 0 ){
pushdown(root,l,r);
}
int mid = MID(l,r);
update(a,b,l,mid,LCHILD(root),add);
update(a,b,mid+1,r,RCHILD(root),add);
pushup(root);
return ;
}

LL query(int a,int b,int l,int r,int root){
if( a > r || b < l ){
return 0;
}
//因为segTree始终是真实状态,直接返回
if( a <= l && r <= b ){
return segTree[root];
}
//查询时也要将需要的部分先更新
if( lazy[root] != 0 ){
pushdown(root,l,r);
}
int mid = MID(l,r);
LL res = 0;
res += query(a,b,l,mid,LCHILD(root));
res += query(a,b,mid+1,r,RCHILD(root));
return res;
}

void pushdown(int root,int l,int r){ //把性质传给儿子 lazy[LCHILD(root)] += lazy[root]; lazy[RCHILD(root)] += lazy[root]; int mid = MID(l,r); //确保儿子segTree真实性 segTree[LCHILD(root)] += lazy[root]*(mid-l+1); segTree[RCHILD(root)] += lazy[root]*(r-mid); //不再具有标记功能 lazy[root] = 0; return ; }

void pushup(int root){
segTree[root] = segTree[LCHILD(root)]+segTree[RCHILD(root)];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树 lazy标记