注意在C++中,加减运算比移位运算优先级高,移位运算比按位运算优先级高。
封装版:
const int MAXM=200000; //MAXM 为线段最大长度
int a[MAXM+5],st[(MAXM<<2)+5]; //a数组为原数组st数组为四倍大小线段树
struct SegmentTree {
int n; //线段树的大小
int op_init=0;
inline int op_cal(int o1,int o2) { return o1+o2; }
void _build(int o,int l,int r) { //o当前结点,l左端点,r右端点,_build(1,1,n)
if(l==r) { st[o]=a[l]; return; }
int m=l+((r-l)>>1);
_build(o<<1,l,m);
_build((o<<1)|1,m+1,r);
st[o]=op_cal(st[o<<1],st[(o<<1)|1]); //子结点建立完成后,更新父节点
}
//单点修改
void _update(int o,int l,int r,int ind,int ans) { //ind为叶结点,ans为值,_update(1,1,n,ind,ans);
if(l==r) { st[o]=ans; return; }
int m=l+((r-l)>>1);
if(ind<=m) _update(o<<1,l,m,ind,ans);
else _update((o<<1)|1,m+1,r,ind,ans);
st[o]=op_cal(st[o<<1],st[(o<<1)|1]); //子节点更新后,更新父节点
}
//查询
int _query(int o,int l,int r,int ql,int qr) { //ql、qr为查询区间左右端点,_query(1,1,n,a,b)
if(ql>r||qr<l) return op_init; //区间不相交,返回无关值
if(ql<=l&&qr>=r) return st[o]; //被查询区间覆盖,返回当前信息
int m=l+((r-l)>>1); //否则综合子结点的信息返回
return op_cal(_query(o<<1,l,m,ql,qr),_query((o<<1)|1,m+1,r,ql,qr));
}
void build(int tn) { n=tn; _build(1,1,n); }
void update(int ind,int ans) { _update(1,1,n,ind,ans); }
int query(int l,int r) { return _query(1,1,n,l,r); }
};
区间加值:
void pushup(int o){ //pushup函数,该函数本身是将当前结点用左右子节点的信息更新,此处求区间和,用于update中将结点信息传递完返回后更新父节点
st[o]=st[o<<1]+st[o<<1|1];
}
void pushdown(int o,int l,int r){ //pushdown函数,将o结点的信息传递到左右子节点上
if(add[o]){ //当父节点有更新信息时才向下传递信息
add[o<<1]+=add[o]; //左右儿子结点均加上父节点的更新值
add[o<<1|1]+=add[o];
int m=l+((r-l)>>1);
st[o<<1]+=add[o]*(m-l+1); //左右儿子结点均按照需要加的值总和更新结点信息
st[o<<1|1]+=add[o]*(r-m);
add[o]=0; //信息传递完之后就可以将父节点的更新信息删除
}
}
void update(int o,int l,int r,int ql,int qr,int addv){ //ql、qr为需要更新的区间左右端点,addv为需要增加的值
if(ql<=l&&qr>=r){ //与单点更新一样,当当前结点被需要更新的区间覆盖时
add[o]+=addv; //更新该结点的所需更新信息
st[o]+=addv*(r-l+1); //更新该结点信息
return; //根据lazy思想,由于不需要遍历到下层结点,因此不需要继续向下更新,直接返回
}
pushdown(o,l,r); //将当前结点的所需更新信息传递到下一层(其左右儿子结点)
int m=l+((r-l)>>1);
if(ql<=m)update(o<<1,l,m,ql,qr,addv); //当需更新区间在当前结点的左儿子结点内,则更新左儿子结点
if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,addv); //当需更新区间在当前结点的右儿子结点内,则更新右儿子结点
pushup(o); //递归回上层时一步一步更新回父节点
}
ll query(int o,int l,int r,int ql,int qr){ //ql、qr为需要查询的区间
if(ql<=l&&qr>=r) return st[o]; //若当前结点覆盖区间即为需要查询的区间,则直接返回当前结点的信息
pushdown(o,l,r); //将当前结点的更新信息传递给其左右子节点
int m=l+((r-l)>>1);
ll ans=0; //所需查询的结果
if(ql<=m)ans+=query(o<<1,l,m,ql,qr); //若所需查询的区间与当前结点的左子节点有交集,则结果加上查询其左子节点的结果
if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr); //若所需查询的区间与当前结点的右子节点有交集,则结果加上查询其右子节点的结果
return ans;
}
区间赋值:只是把pushdown和update的+=改成=
void pushup(int o){
st[o]=st[o<<1]+st[o<<1|1];
}
void pushdown(int o,int l,int r){ //pushdown和区间加值不同,改值时修改结点信息只需要对修改后的信息求和即可,不用加上原信息
if(change[o]){
int c=change[o];
change[o<<1]=c;
change[o<<1|1]=c;
int m=l+((r-l)>>1);
st[o<<1]=(m-l+1)*c;
st[o<<1|1]=(r-m)*c;
change[o]=0;
}
}
void update(int o,int l,int r,int ql,int qr,int c){
if(ql<=l&&qr>=r){ //同样更新结点信息和区间加值不同
change[o]=c;
st[o]=(r-l+1)*c;
return;
}
pushdown(o,l,r);
int m=l+((r-l)>>1);
if(ql<=m)update(o<<1,l,m,ql,qr,c);
if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,c);
pushup(o);
}
int query(int o,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r) return st[o];
pushdown(o,l,r);
int m=l+((r-l)>>1);
int ans=0;
if(ql<=m)ans+=query(o<<1,l,m,ql,qr);
if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr);
return ans;
}
线段树区间异或:每一位开一个线段树,每个节点维护对应线段的异或和。
好像主席树(可持久化线段树)也是线段树?
解决第k大问题应该用什么?应该比划分树好,划分树看起来是静态的,主席树只是常数稍大。
压行版:
const int MAXM=200000;
int a[MAXM+5],st[(MAXM<<2)+5];
void build(int o,int l,int r){
if(l==r) st[o]=a[l];
else{
int m=l+((r-l)>>1);
build(o<<1,l,m);
build(o<<1|1,m+1,r);
st[o]=max(st[o<<1],st[o<<1|1]);
}
}
void update(int o,int l,int r,int id,int v){
if(l==r) st[o]=v;
else{
int m=l+((r-l)>>1);
if(id<=m) update(o<<1,l,m,id,v);
else update(o<<1|1,m+1,r,id,v);
st[o]=max(st[o<<1],st[o<<1|1]);
}
}
int query(int o,int l,int r,int a,int b){
if(r<a||l>b) return -1;
if(a<=l&&r<=b) return st[o];
int m=l+((r-l)>>1);
int p1=query(o<<1,l,m,a,b),p2=query(o<<1|1,m+1,r,a,b);
return max(p1,p2);
}
扫描线线段树,注意每个点坐标包含它右边一单位长度的线段(整数扫描线?)

本文深入探讨了C++中的线段树数据结构,包括基本的构建、查询和更新操作,以及如何处理区间加值、区间赋值和区间异或等复杂操作。文章详细解释了pushdown和pushup函数的作用,以及它们在更新过程中的应用。
705





