线段树模板全面
Posted iuk11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树模板全面相关的知识,希望对你有一定的参考价值。
过题之前先把cin换成scanf
点/区间线段树求和
线段树,顾名思义,是把给定的n个元素组成的序列比作线段,以树的方式重建,进而达到更新,询问的时间复杂度都达到
log
2
n
\\log_2n
log2n的级别。
pushup
建树过程中或者更新过程中,会先递归到叶子节点(不一定到叶子节点),然后再回溯,就是下一次回到他们两个的子树根节点,该点要记录两个子节点的和,所以pushup.当然再向上回溯同理,也是当前根等于他两个子节点的权值和。
pushdown
当我们更新区间的时候,不需要把该区间需要加的数传递下去更新,只需要用lazy[]数组存放在当前区间上面即可。如果我需要访问这个区间内的子区间,就需要把lazy[]中的值传递下去(如果有值),因为递归回来会更新权值和,两个子节点相加的和会覆盖掉当前节点的值,如果不下传,会产生子树和错误。
void pushup(int rt){
ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void pushdown(int rt,int ln,int rn){
//ln rn 左 右 子树的节点个数
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
ans[rt<<1]+=lazy[rt]*ln;
ans[rt<<1|1]+=lazy[rt]*rn;
lazy[rt]=0;
}
}
build
首先先递归到叶子节点,然后从叶子结点开始向上回溯。每个叶子节点都对应着其中的一个输入值,我们不断二分回溯将子树和记录在当前的子树根上,最后完成整棵树的建立。
void build(int l,int r,int rt){
if(l==r){
ans[l]=a[l];
return ;
}
int mid=(l+r)>>1;
build(1,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
整体代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
ll a[N],ans[N<<2],lazy[N<<2];
int n,q;
int l,r;
void pushup(int rt){
ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
ans[l]=a[l];
return ;
}
int mid=(l+r)>>1;
build(1,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn){
//ln rn 左 右 子树的节点个数
if(lazy[rt]){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
ans[rt<<1]+=lazy[rt]*ln;
ans[rt<<1|1]+=lazy[rt]*rn;
lazy[rt]=0;
}
}
void add(int L,int c,int l,int r,int rt){
//L点 +c
if(l==r){
ans[rt]+=c;
return ;
}
int mid=(l+r)>>1;
//pushdown(rt,mid-l+1,r-mid);
if(L<=mid) add(L,c,l,mid,rt<<1);
else add(L,c,mid+1,r,rt<<1|1);
pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
ans[rt]+=c*(r-l+1);
lazy[rt]+=c;
return ;
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if(L<=mid) update(L,R,c,l,mid,rt<<1);
if(R>mid) update(L,R,c,mid+1,r,rt<<1|1);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return ans[rt];
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
ll ans=0;
if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
cin>>q;
while(q--){
int c;
cin>>l>>r;
cin>>c;
//add(l,c,1,n,1); 点更新
//update(l,r,c,1,n,1); 区间更新
//query(l,r,1,n,1); 区间查询
}
return 0;
}
点/区间线段树求最大值
hdu 1754 模板题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
ll a[N],ans[N<<2],lazy[N<<2];
int n,q;
int l,r;
void pushup(int rt){
ans[rt]=max(ans[rt<<1],ans[rt<<1|1]);
}
void build(int l,int r,int rt){//建树
if(l==r){
ans[rt]=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void add(int L,int c,int l,int r,int rt){
//L点 +c
if(l==r){
ans[rt]=c;
return ;
}
int mid=(l+r)>>1;
if(L<=mid) add(L,c,l,mid,rt<<1);
else add(L,c,mid+1,r,rt<<1|1);
pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
ans[rt]=c;
return ;
}
int mid=(l+r)>>1;
if(L<=mid) update(L,R,c,l,mid,rt<<1);
if(R>mid) update(L,R,c,mid+1,r,rt<<1|1);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return ans[rt];
}
int mid=(l+r)>>1;
ll res=0;
if(L<=mid) res=max(res,query(L,R,l,mid,rt<<1));
if(R>mid) res=max(res,query(L,R,mid+1,r,rt<<1|1));
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
cin>>q;
while(q--){
int opt;
cin>>opt>>l>>r;
//cin>>c;
//add(l,c,1,n,1); 点更新
//update(l,r,c,1,n,1); 区间更新
//query(l,r,1,n,1); 区间查询
if(opt==1){
cout<<query(l,r,1,n,1)<<endl;
}else{
int c;
cin>>c;
update(l,r,c,1,n,1);
}
}
return 0;
}
查询最大值以及最大值个数
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
typedef long long ll;
int n,m;
ll w[N],ans[N<<2],lazy[N<<2];
int cnt[N<<2];
void pushup(int rt){
ans[rt]=max(ans[rt<<1],ans[rt<<1|1]);
if(ans[rt<<1]==ans[rt<<1|1]){
cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1];
}else if(ans[rt<<1] > ans[rt<<1|1]){
cnt[rt]=cnt[rt<<1];
}else{
cnt[rt]=cnt[rt<<1|1];
}
}
void build(int l,int r,int rt){
if(l==r){
ans[rt]=w[l];
cnt[rt]=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void add(int L,int c,int l,int r,int rt){
if(l==r&&l==L){
ans[rt]=c;
cnt[rt]=1;
return ;
}
int mid=(l+r)>>1;
if(L<=mid) add(L,c,l,mid,rt<<1);
else add(L,c,mid+1,r,rt<<1|1);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return ans[rt];
}
int mid=(l+r)>>1;
ll res=0;
if(L<=mid) res=max(res,query(L,R,l,mid,rt<<1));
if(R>mid) res=max(res,query(L,R,mid+1,r,rt<<1|1));
return res;
}
ll res1=0;
int getnum(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
if(res1<=ans[rt]){
return cnt[rt];
}else{
return 0;
}
}
int z=0;
int mid=(l+r)>>1;
if(L<=mid) z+=以上是关于线段树模板全面的主要内容,如果未能解决你的问题,请参考以下文章