数据结构之线段树
线段树
一、性质:
完全二叉树,每一个节点是一个区间,左儿子是左半个区间,右儿子是右半个区间;
可以维护区间最值和区间和
二、常用宏定义
#define Mid ((l+r)>>1) //注意括号 #define lson rt<<1,l,Mid //左结点 #define rson rt<<1|1,Mid+1,r //右结点
三、建树
建树丛根结点开始,递归建立左右子树,直到叶子结点,然后反向赋值,父结点的值 = F(左结点的值,右结点的值),这个F是依据题意变的,如果是区间最大则为max()
void build(int rt,int l,int r) //建编号为rt的区间为[l,r]的树,主函数传进来的固定是(1,1,n) { if(l==r){ //叶子结点赋初值,注意下标,Max的是编号,val原数组的是l,看图可以理解 Max[rt] = val[l]; }else{ //建左右子树 build(lson); build(rson); Max[rt] = max( Max[rt<<1], Max[rt<<1|1]); //父结点Max值为Max(左子结点,右子结点) } }
四、查询
查询为区间查询(只是查询某个点的话不需要线段树),即在区间里查询某个特性值,每次查询都是从跟结点开始往下,根据查询区间和当前区间的区间位置判断是要去左右子区间查询,还是直接返回。如果被查询区间是查询区间的子区间则直接返回子区间的值,如在[1,6]里查询[1,12]就返回[1,6]的值,不再往下查询。
void query(int rt,int l,int r,int L,int R) //在[l,r]里查询[L,R]的值,[L,R]一直不变,[l,r]变 { if(L <= l && r <= R){ ans1 = max(ans1,Max[rt]); ans2 = min(ans2,Min[rt]); }else{ if( L <= Mid) //查询区间在当前区间的左半区间有内容,如在[1,6]里查询[2,3] query(lson,L,R); if( R > Mid) //同理去右子区间,注意不能有else,因为有横跨左右的情况,如[1,6]里查询[2,5] query(rson,L,R); } }
五、单点更新
void update(int rt,int l,int r,int pos,int num) { if(l == r && r == pos){ //到对应的叶结点 Max[rt] = num; }else{ if( pos <= Mid) update(lson,pos,num); if( pos > Mid) //或者直接else,点不可能同时在两个区间里 update(rson,pos,num); Max[rt] = max( Max[rt<<1], Max[rt<<1|1]); } }
六、区间成段更新
Lazy思想就是更新到某个区间的时候,就先给这个区间打上标记,标记内容是需要更新的值,并把子区间的值改为子区间对应的值,清除该区间的lazy标记;然后return,不去更新子区间。当下一次更新或查询等需要访问该区间的子区间的时候再把该区间的lazy和其他信息送回子区间。
void pushDown(int rt,int len) { add[rt<<1] = add[rt<<1|1] = add[rt]; sum[rt<<1] = (len-(len>>1))*add[rt]; sum[rt<<1|1] = (len>>1)*add[rt]; add[rt] = 0; } void update(int rt,int l,int r,int L,int R,int z) { if(L <= l && r <= R){ add[rt] = z; sum[rt] = (r-l+1)*z; }else{ if(add[rt]) pushDown(rt,r-l+1); if(L <= Mid) update(lson,L,R,z); if(R > Mid) update(rson,L,R,z); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }
区间更改区间查询的板子
#include<iostream> #include<cstdio> #define MAXN 1000001 #define ll long long using namespace std; unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2]; 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 build(ll p,ll l,ll r) { tag[p]=0; if(l==r) { ans[p]=a[l]; return ; } ll mid=(l+r)>>1; build(ls(p),l,mid); build(rs(p),mid+1,r); push_up(p); } inline void f(ll p,ll l,ll r,ll k) { tag[p]=tag[p]+k; ans[p]=ans[p]+k*(r-l+1); } 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; cin>>n>>m; for(ll i=1; i<=n; i++)scanf("%lld",&a[i]); build(1,1,n); while(m--) { scanf("%lld",&a1); if(a1==1) { scanf("%lld%lld%lld",&b,&c,&d); update(b,c,1,n,1,d); } else { scanf("%lld%lld",&e,&f); printf("%lld\n",query(e,f,1,n,1)); } } return 0; }