先說樹狀數組吧 主要有lowbit,update,getsum
int lowbit(int x) { return x&(-x); }
int update(int x,int date) { while(x<=y) { c[x]+=date; x+=lowbit(x); } }
int getsum(int x) { int ans=0; while(x>0) { ans+=c[x]; x-=lowbit(x); } return ans; }
lowbit的作用就是找到該節點的父節點或子節點 圖 (https://www.cnblogs.com/George1994/p/7710886.html)

注意了 a數組存的是原來的數 c數組的意義代表着c[i] 就是前i項的和
線段樹的話

差不多兩個數組搞定一個tree【n】和一個add【n】
先是建立樹
線段樹是從1這個節點開始的
void build(int l,int r,int k)//l,r為建樹的范圍 { if(l==r) { cin>>tree[k]; return ; } int m=(l+r)>>1; build(l,m,k<<1);//遞歸建立數 build(m+1,r,k<<1|1); tree[k]=tree[k<<1]+tree[k<<1|1];//求和 }
自我覺得這個是比較靈活的,對這個改動一下就成為不同的功能,比如最大值
void build(int l,int r,int k) { if(l==r) { cin>>tree[k]; return ; } int m=(l+r)>>1; build(l,m,k<<1); build(m+1,r,k<<1|1); //tree[k]=tree[k<<1]+tree[k<<1|1]; tree[k]=max(tree[k<<1],tree[k<<1|1]); }
這樣就變成了父節點 為子節點的最大值
然后是點更新
void update(int ee,int c,int l,int r,int k) { if(l==r) { tree[k]+=c; //tree[k]=c; 這樣就是更改這個數,而不是加 return ; } int m=(l+r)>>1; if(ee<=m) update(ee,c,l,m,k<<1);//ee為需要更新的葉子節點,通過遞歸找 else update(ee,c,m+1,r,k<<1|1); tree[k]=tree[k<<1]+tree[k<<1|1]; }
然后是區間更新 ,這里需要懶標記,這個確實比較難理解
void update(int x,int y,int c,int l,int r,int k)//x,y為需要更新區間,l,r為k這一節點的管理范圍 { if(x<=l&&y>=r) { tree[k]=c*(r-l+1);//這一點為c乘以這一點管理的數量 add[k]=c;//把這一點標記一下,為懶標記 return;//注意是結束了 } int m=(l+r)>>1; pushdown(k,m-l+1,r-m);//下推標記 if(x<=m) update(x,y,c,l,m,k<<1); if(y>m) update(x,y,c,m+1,r,k<<1|1);//注意 這里不是else 而是都要更新並查找 tree[k]=tree[k<<1]+tree[k<<1|1]; }
void pushdown(int k,int ln,int rn)//ln,rn為左子樹,右子樹的數字數量。 { if(add[k]) { add[k<<1]+=add[k]; add[k<<1|1]+=add[k];//傳遞這個標記 sum[k<<1]+=add[k]*ln; sum[k<<1|1]+=add[k]*rn; add[k]=0; } }
上面是要求和的下推標記,但如果說是這道題
為這個區間都改變成一樣的數,而不是相加一樣的數
void pushdown(int k,int ln,int rn){ if(add[k]){ add[k<<1]=add[k]; add[k<<1|1]=add[k];
sum[k<<1]=add[k]*ln; sum[k<<1|1]=add[k]*rn; add[k]=0; } }
最后是區間求和
int query(int x,int y,int l,int r,int k) { if(x<=l&&r<=y) { return tree[k]; } int m=(l+r)>>1; pushdown(k,m+1-l,r-m); int ans=0; if(x<=m) ans+=query(x,y,l,m,k<<1); if(y>m) ans+=query(x,y,m+1,r,k<<1|1); return ans; }
