数据结构学习~加法线段树

xiaoxiao2021-08-30  1.4K+

前言

本文所有内容出自于个人的理解,可能有谬误,还请多多包涵,敬请指正。qq :2039316792 本文中参考的所有文章,将会在专页中一一列出。

正文

用途

区间修改单点修改区间查询单点查询

时间复杂度

O( nlogn )

概论

使用方法

懒标记

懒标记的懒在于用它的时候它才发挥作用,不用的时候就闲着。OI得讲,当我们需要更新子节点时,我们才将标记下放,否则,我们将其存储起来。

找儿子

inline ll ls(ll x){return x<<1;} inline ll rs(ll x){return x<<1|1;}

建树

递归建树,若当前节点是叶节点,则用输入的值更新答案数组

void built(ll p,ll l,ll r) { tag[p]=0; if(l==r){ans[p]=a[l];return ;} ll mid=(l+r)>>1; built(ls(p),l,mid); built(rs(p),mid+1,r); push_up(p); }

上传

我们将左儿子的值与右儿子的值相加,得到父亲的值,即是上传。

inline void push_up(ll p){ans[p]=ans[ls(p)]+ans[rs(p)];}

标记下放

当我们询问某一个区间的值得时候,我们储存的值显然与目标值不同,所以,我们要将父亲节点的懒标记下放,在寻找目标区间的时候,顺便将目标区间的值修改为目标值。

inline void f(ll p,ll l,ll r,ll k) { tag[p]+=k; ans[p]=ans[p]+(r-l+1)*k; } inline void push_down(ll p,ll l,ll r) { ll mid=(l+r)>>1; f(ls(p),l,mid,tag[p]); f(rs(p),mid+1,r,tag[p]); tag[p]=0; }

区间修改

若当前的区间已经恰好完全覆盖了目标区间,则返回答案。否则,我们将目标区间分为当前区间和其他区间,递归求其他区间的答案。

inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k) { if(nl<=l&&r<=nr) { ans[p]+=k*(r-l+1); tag[p]+=k; return ; } push_down(p,l,r); ll mid=(l+r)>>1; if(nl<=mid)update(nl,nr,l,mid,ls(p),k); if(nr>mid)update(nl,nr,mid+1,r,rs(p),k); push_up(p); }

代码

以下代码以洛谷P3372 【模板】线段树 1为例

#include<cstdio> #define maxn 100100 #define ll long long using namespace std; unsigned ll a[maxn],tag[maxn<<2],ans[maxn<<2],n,m; inline ll ls(ll x){return x<<1;} inline ll rs(ll x){return x<<1|1;} inline void push_up(ll p){ans[p]=ans[ls(p)]+ans[rs(p)];} void input() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++)scanf("%lld",&a[i]); } void built(ll p,ll l,ll r) { tag[p]=0; if(l==r){ans[p]=a[l];return ;} ll mid=(l+r)>>1; built(ls(p),l,mid); built(rs(p),mid+1,r); push_up(p); } inline void f(ll p,ll l,ll r,ll k) { tag[p]+=k; ans[p]=ans[p]+(r-l+1)*k; } inline void push_down(ll p,ll l,ll r) { ll mid=(l+r)>>1; f(ls(p),l,mid,tag[p]); f(rs(p),mid+1,r,tag[p]); tag[p]=0; } inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k) { if(nl<=l&&r<=nr) { ans[p]+=k*(r-l+1); tag[p]+=k; return ; } push_down(p,l,r); ll mid=(l+r)>>1; if(nl<=mid)update(nl,nr,l,mid,ls(p),k); if(nr>mid)update(nl,nr,mid+1,r,rs(p),k); push_up(p); } ll query(ll q_x,ll q_y,ll l,ll r,ll p) { ll res=0; if(q_x<=l&&r<=q_y)return ans[p]; ll mid=(l+r)>>1; push_down(p,l,r); if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p)); if(q_y>mid)res+=query(q_x,q_y,mid+1,r,rs(p)); return res; } int main() { ll a1,b,c,d,e,f; input();built(1,1,n); while(m--) { scanf("%lld",&a1); switch(a1) { case 1 : scanf("%lld%lld%lld",&b,&c,&d);update(b,c,1,n,1,d);break; case 2 : scanf("%lld%lld",&e,&f);printf("%lld\n",query(e,f,1,n,1));break; } } return 0; }

参考文献

后记

线段树真…令人爱恨交加…成也线段树,败也线段树…能用树状数组的时候千万不要用线段树啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!