1、
(1)、Lazy思想:对整个结点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分到下层。
(2)、延迟标记 Lazy:如果需要对一个区间中每一个叶结点进行操作,我们不妨先别忙着操作,而是在所有大区间上做一个标记, 下一次遇到或要用到时,再进行处理(标记传递),达到减少操作次数,提高线段树的效率的作用。
2、
NKOJ 2297 数列操作2
时间限制 : 10000 MS 空间限制 : 165536 KB
问题描述
给出一列数{Ai}(1≤i≤n),总共有m次操作,操作分如下两种:
1.ADD x y z 将x到y区间的所有数字加上z
2.ASK x y 将x到y区间的最大一个数字输出
输入格式
第一行,一个整数n
第二行,n个空格间隔的整数,表示数列一开始的情形
第三行,一个整数m
接下来m行,表示m条操作
样例输入
5
1 2 3 2 5
5
ADD 1 4 3
ASK 2 3
ASK 3 5
ADD 2 4 2
ASK 2 5
样例输出
6
6
8
提示
m,n<=100000
x,y<=100000
z<=1000
思路
lazy
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int need=200003;//数据有鬼
struct fy{int a,b,val,lazy;}t[need*4];
int a[need];
int x,y,z;
void build(int s,int x,int y)
{
t[s].a=x,t[s].b=y;
if(x==y)
{
t[s].val=a[x];
return;
}
build(s<<1,x,(x+y)/2),build((s<<1)|1,(x+y)/2+1,y);
t[s].val=max(t[s<<1].val,t[(s<<1)|1].val);
}
void putdown(int s)
{
if(t[s].a==t[s].b) return;
int k=t[s].lazy;
t[s].lazy=0;
t[s<<1].val+=k;
t[s<<1].lazy+=k;
t[(s<<1)|1].val+=k;
t[(s<<1)|1].lazy+=k;
}
void add(int s)
{
if(t[s].lazy!=0) putdown(s);
if(x<=t[s].a&&t[s].b<=y)
{
t[s].lazy+=z;
t[s].val+=z;
return ;
}
if(!(y<t[s<<1].a||x>t[s<<1].b)) add(s<<1);
if(!(y<t[(s<<1)|1].a||x>t[(s<<1)|1].b)) add((s<<1)|1);
t[s].val=max(t[s<<1].val,t[(s<<1)|1].val);
}
int getmax(int s)
{
if(t[s].lazy!=0) putdown(s);
if(x<=t[s].a&&t[s].b<=y) return t[s].val;
int ans=-need;
if(!(y<t[s<<1].a||x>t[s<<1].b)) ans=max(ans,getmax(s<<1));
if(!(y<t[(s<<1)|1].a||x>t[(s<<1)|1].b)) ans=max(ans,getmax((s<<1)|1));
return ans;
}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
int m;scanf("%d",&m);
string a;
for(int i=1;i<=m;i++)
{
cin>>a;
if(a=="ADD")
{
scanf("%d%d%d",&x,&y,&z);
add(1);
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",getmax(1));
}
}
}
2、
NKOJ 2295 涂色
时间限制 : 10000 MS 空间限制 : 165536 KB
问题描述
在数轴上进行一系列操作。每次操作有两种类型,一种是在线段[a,b]上涂上颜色,另一种将[a,b]上的颜色擦去。
问经过一系列的操作后,有多少条单位线段[k,k+1]被涂上了颜色。
输入格式
第一行,一个整数n,表示总的操作数
以后n行输入三个整数(n<=100000)。
第一个数为1表示涂色,0表示擦去。
第二、三个数表示线段a,b。
输出格式
有多少条单位线段[k,k+1]被涂上了颜色。
输入样例1:
5
1 1 15
0 4 9
1 7 18
1 7 9
0 1 3
输入样例2:
4
1 1 8
0 4 8
1 7 8
0 1 3
输出样例1:
12
输出样例2:
2
思路
1、因为题所求为线段数量,而以点为叶节点不好讨论,所以叶节点表示一个单位线段,即[x,mid]、[mid,y]分别为左儿子和右儿子表示的区间。
注意a+1==b时才表示为叶节点。
2、lazy不仅存储要下放的状态还表示该区间的状态。
因为状态较少,且子节点和父节点状态很可能一致,所以才这样处理。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int need=200013;
struct fy{int a,b,v,lazy;}t[need*2]; //-1 clean 1 paint 0 表示该区间中既有涂了颜色的也有没涂的
int c[need>>1],le[need*2],ri[need*2];
int tot=0,x,y;
void build(int x,int y)
{
int s=++tot;
t[s].a=x,t[s].b=y;
if(x+1==y)
{
t[s].lazy=-1;//一开始应全未涂色
return ;
}
le[s]=tot+1;build(x,(x+y)>>1);
ri[s]=tot+1;build((x+y)>>1,y);
}
void putdown(int s)
{
if(t[s].a+1==t[s].b) return;//不判断就下放,会越界
t[le[s]].lazy=t[ri[s]].lazy=t[s].lazy;//无论之前什么状态都可以直接覆盖
t[s].lazy=0;
}
void clean(int s)
{
if(t[s].lazy<0) return ;
if(x<=t[s].a&&t[s].b<=y)
{
t[s].lazy=-1;
return ;
}//如果“putdown”中没判断是否为叶节点,这里就要先判断。
if(t[s].lazy>0) putdown(s);
if(!(t[le[s]].a>y||t[le[s]].b<x)) clean(le[s]);
if(!(t[ri[s]].a>y||t[ri[s]].b<x)) clean(ri[s]);
}
void paint(int s)
{
if(t[s].lazy>0) return ;
if(x<=t[s].a&&t[s].b<=y)
{
t[s].lazy=1;
return ;
}
if(t[s].lazy<0) putdown(s);
if(!(t[le[s]].a>y||t[le[s]].b<x)) paint(le[s]);
if(!(t[ri[s]].a>y||t[ri[s]].b<x)) paint(ri[s]);
}
int solve(int s)
{
if(t[s].lazy>0) return t[s].b-t[s].a;//因为树中叶节点为线段,所以线段数量为点数减一,即该处不用加一
if(t[s].lazy<0) return 0;
return solve(le[s])+solve(ri[s]);//因为叶节点不会出现lazy=0的情况,所以不用判断s是否为叶节点
}
int main()
{
int n;scanf("%d",&n);
build(1,need-13);
for(int a,i=1;i<=n;i++)
{
scanf("%d%d%d",&a,&x,&y);
if(a) paint(1);
else clean(1);
}
printf("%d",solve(1));
}
相似题:
http://blog.csdn.net/Y__XV/article/details/51890624 (lazy 相似)
http://blog.csdn.net/Y__XV/article/details/51913412