标签:递归 包括 不能 register 社会 build 相同 更新 return
刚刚学习了一下$ZKW$线段树
一个非常社会的写法(非递归)终于不用蓝屏了
这个神奇的线段树,,,是从下向上建的。。。
如题,,,
第一个循环是求log2(n+1)
inline void build(){
scanf("%d",&n);
for(;N<=n+1;N<<=1);//求N
for(register int i=N+1;i<=n+N;++i){
scanf("%lld",tree+i);//scanf("%d",&tree[i])
}
for(register int i=N-1;i>=1;--i){
tree[i]=tree[i<<1]+tree[i<<1|1];//tree[i]=tree[i/2]+tree[i/2+1]
}
}
然后是单点修改,,,其实就是暴力
inline void add(int x,int k) {//x位置上+n
for(x+=N;x;x>>=1){
tree[x]+=k;
}
}
区间修改的话只能加或乘但是不能乘加
这里我们采用标记永久化的思想(就是不下推lazy标记就让他在那里扑街)
inline void update(int s,int t,int k) {//s左边界,t右边界
int lnum=0,rnum=0,nnum=1;//左包括数量,右包括数量,总数量
for(s=N+s-1,t=N+t+1;s^t^1;s>>=1,t>>=1,nnum<<=1){//判断s和t的父节点是否一样,一样就停下,若父节点相同s^t^1=0,计算st左右边际
tree[s]+=k*lnum;//更新左右边界内的值
tree[t]+=k*rnum;
if(~s&1){//计算左右支的数量
add[s^1]+=k;
tree[s^1]+=k*nnum;
lnum+=nnum;
}
if(t&1){
add[t^1]+=k;
tree[t^1]+=k*nnum;
rnum+=nnum;
}
}
for(;s;s>>=1,t>>=1) {//向上维护根节点
tree[s]+=k*lnum;
tree[t]+=k*rnum;
}
}
区间查询
跟区间修改差不多
要注意s,t每次上推时都要根据当前所在节点的标记和lNum / rNum更新$ans (ans += add[s]*lNum)$
inline long long query(int s, int t){
int lnum=0,rnum=0,nnum=1;
long long ans=0;
for(s=N+s-1,t=N+t+1;s^t^1;s>>=1,t>>=1,nnum<<=1){
if(add[s]){
ans+=add[s]*lnum;
}
if(add[t]){
ans+=add[t]*rnum;
}
if(~s&1){
ans+=tree[s^1];
lnum+=nnum;
}
if(t&1){
ans+=tree[t^1];
rnum+=nnum;
}
}
for(;s;s>>=1,t>>=1){
ans+=add[s]*lnum;
ans+=add[t]*rnum;
}
return ans;
}
差不多就是这样了
,,,
标签:递归 包括 不能 register 社会 build 相同 更新 return
原文地址:https://www.cnblogs.com/snzy/p/9782127.html