线段树
来源:互联网 发布:恶灵附身优化怎么样 编辑:程序博客网 时间:2024/06/01 08:14
最大值(区间修改)
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
描述
在N(1<=n<=100000)个数A1…An组成的序列上进行M(1<=m<=100000)次操作,操作有两种:
(1)1 LR C:表示把A[L]到A[R]增加C(C的绝对值不超过10000);
(2)2 LR:询问A[L]到A[R]之间的最大值。
输入
第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入M(1<=M<=100000)表示操作的次数,接下来M行,每行为1 L R C或2 L R
输出
对于每个操作(2)输出对应的答案。
样例输入
51234532 1 41 1 3 32 3 5
样例输出
46
提示
保证序列中的所有的数都在longint范围
这道题是最基本的区间修改问题,并且只需要求最大值。在开始之前,我们先明确一下需要的数据和对应的含义。
我们需要一颗用于保存最大值的线段树tree和懒惰标记树lazy,lazy[i]的含义是i结点对应的区间中的每个值都加上lazy[i]。注意,假设我们只进行了一次1操作,那么此时如果lazy[i]不为0,则lazy[i*2]和lazy[i*2+1]必为0;反之,如果lazy[i*2]和lazy[i*2+1]不为0,那么lazy[i]必为0。因为在同一时刻我们只保存一个操作,如果需要我们再分解操作。我们把置lazy[i]为0并更新lazy[i*2]和lazy[i*2+1]等其他数据的操作叫做pushdown,即把操作从一个结点往下推,往下分解。
这么看来,tree的含义就是:如果tree[i]已经因pushdown更新,那么它代表i结点对应的区间中的最大值;如果没有更新,那就从树的根部开始一直更新下来。以上的含义还是相对抽象,让我们从代码中分析下。
先找题意声明一下变量:
const int maxn=100005;int tree[maxn*3];int lazy[maxn*3];int n,m;
由于线段树在一开始没有任何更新操作,根据定义它就代表对应区间的最大值,跟点修改线段树一样,没有特殊含义,所以我们可以递归输入建树:
void build(int node=1, int l=1, int r=n){ if(l==r) { scanf("%d",&tree[node]); return; } int mid=(l+r)/2; build(node<<1, l, mid); build((node<<1)+1, mid+1, r); tree[node]=std::max(tree[node<<1], tree[(node<<1)+1]);}
这个代码应在输入n之后,输入m之前运行,没什么好说的。
然后是操作1,我们姑且叫它change函数吧。为了节省一点栈空间,我们把待修改区间和增加的值声明为全局变量:
int g_L,g_R,g_Add;void change(int node=1, int l=1, int r=n){ if(g_L<=l && r<=g_R) { tree[node]+=g_Add; //这个结点对应线段的所有点都加上了g_Add,所以最大值也加g_Add lazy[node]+=g_Add; //我们只操作这个结点,而不递归传下去,因为这时我们传下去了也用不到,所以通过lazy保存结点对应线段每个点的增加值 return; } int mid=(l+r)/2; int lc=node<<1; int rc=(node<<1)+1; //现在要更新子结点了对吧,既然子结点的最大值还没有加上g_Add,那我们怎么知道加了后的值是多少呢? pushdown(node); //那就更新它,把lazy记号推下去 if(g_L<=mid) change(lc, l, mid); if(g_R>mid) change(rc, mid+1, r); tree[node]=std::max(tree[lc],tree[rc]); //记住要回来更新父结点 }
既然已多次说道pushdown函数,就让我们来看看他的代码:
void pushdown(int node){ if(lazy[node]) { lazy[node<<1]+=lazy[node]; lazy[(node<<1)+1]+=lazy[node]; tree[node<<1]+=lazy[node]; tree[(node<<1)+1]+=lazy[node]; lazy[node]=0; }}
注意,pushdown的实质就是把操作1分解,让子结点保存分解后的操作,这样就不必进行所有的操作,因为大部分都是无效的。除了让子结点的lazy值加上父结点(当前结点)的lazy值,子结点的最大值tree也要加上lazy值。再次强调:我们的tree保存对应区间的最大值,在正常访问tree结点时,则它已经加上了该加的值,lazy是保存子结点还没有加上的值的。多领会一下。
最后是查询操作:
//使用g_L和g_Rint query(int node=1, int l=1, int r=n){ if(g_L<=l && r<=g_R) { return tree[node]; //注意tree[node]的含义:我们已经保证tree[node]已经更新,所以答案就是tree[node],不要再加上lazy[node],它是作用于子结点的 } int mid=(l+r)/2; int lc=node<<1; int rc=(node<<1)+1; pushdown(node); //查询时也要更新,以把加上的值记录在内 int ans=0x80000000; if(g_L<=mid) ans=std::max(ans, query(lc, l, mid)); if(g_R>mid) ans=std::max(ans, query(rc, mid+1, r)); return ans;}
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;const int maxn=100005;int tree[maxn*3];int lazy[maxn*3];int n,m;void build(int node=1, int l=1, int r=n){ if(l==r) { scanf("%d",&tree[node]); return; } int mid=(l+r)/2; build(node<<1, l, mid); build((node<<1)+1, mid+1, r); tree[node]=std::max(tree[node<<1], tree[(node<<1)+1]);}void pushdown(int node){ if(lazy[node]) { lazy[node<<1]+=lazy[node]; lazy[(node<<1)+1]+=lazy[node]; tree[node<<1]+=lazy[node]; tree[(node<<1)+1]+=lazy[node]; lazy[node]=0; }}int g_L,g_R,g_Add;void change(int node=1, int l=1, int r=n){ if(g_L<=l && r<=g_R) { tree[node]+=g_Add; //这个结点对应线段的所有点都加上了g_Add,所以最大值也加g_Add lazy[node]+=g_Add; //我们只操作这个结点,而不递归传下去,因为这时我们传下去了也用不到,所以通过lazy保存结点对应线段每个点的增加值 return; } int mid=(l+r)/2; int lc=node<<1; int rc=(node<<1)+1; //现在要更新子结点了对吧,既然子结点的最大值还没有加上g_Add,那我们怎么知道加了后的值是多少呢? pushdown(node); //那就更新它,把lazy记号推下去 if(g_L<=mid) change(lc, l, mid); if(g_R>mid) change(rc, mid+1, r); tree[node]=std::max(tree[lc],tree[rc]); //记住要回来更新父结点 }//使用g_L和g_Rint query(int node=1, int l=1, int r=n){ if(g_L<=l && r<=g_R) { return tree[node]; //注意tree[node]的含义:我们已经保证tree[node]已经更新,所以答案就是tree[node],不要再加上lazy[node],它是作用于子结点的 } int mid=(l+r)/2; int lc=node<<1; int rc=(node<<1)+1; pushdown(node); //查询时也要更新,以把加上的值记录在内 int ans=0x80000000; if(g_L<=mid) ans=std::max(ans, query(lc, l, mid)); if(g_R>mid) ans=std::max(ans, query(rc, mid+1, r)); return ans;}int main(){ scanf("%d",&n); build(); scanf("%d",&m); while(m--) { int operation, l, r, value; scanf("%d%d%d", &operation, &l, &r); if(operation==1) { scanf("%d", &value); g_L=l; g_R=r; g_Add=value; change(); } else if(operation==2) { g_L=l; g_R=r; printf("%d\n",query()); } } return 0;}
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 进一步解析“Hello World”程序
- 线程互斥与同步(part2)—互斥锁(Mutex)的“cp”:条件变量(Condition Variable)
- POJ 2955 Brackets——区间dp
- CPU百分百问题的研究
- MySQL 数据库基本命令汇总
- 线段树
- Java基础之——字母大小写转换
- 811B
- HTML练习2
- PAT 甲级 1006. Sign In and Sign Out
- Linux下查看内存使用情况方法总结
- oracle中序列详解(转)
- 欢迎使用CSDN-markdown编辑器
- Mybatis获取插入记录的自增长ID