线段树模板
线段树是一种常用的算法,本人近日试着学习了一下,发现好难啊。。
下面是我学习了几天打的模板
1 int a[100001],sum[100001]; //sum表示线段树,空间O(2n),a表示序列 2 3 //建立一颗线段树 4 void change(int root){ 5 sum[root]=sum[root*2]+sum[root*2+1]; 6 } 7 void buildtree(int root,int l,int r){ 8 if (l==r) { 9 sum[root]=a[l]; return; 10 } 11 int mid= l+r>>1; 12 buildtree(root*2,l,mid); 13 buildtree(root*2+1,mid+1,r); 14 change(root); 15 } 16 17 //单点修改q 18 void change(int root){ 19 sum[root]=sum[root*2]+sum[root*2+1]; 20 } 21 void change (int root,int l,int r,int p,int q){ 22 if (l==r) { 23 sum[root]+=q; 24 return; 25 } 26 int mid=l+r>>1; 27 if (p<=mid) change(root*2,l,mid,p,q); 28 else change(root*2+1,mid+1,r,p,q); 29 change(root); 30 } 31 32 //求区间和[L,R] 33 int find(int root,int l,int r,int L,int R){ 34 if (r<L||l>R) return 0; 35 if (l>=L&&r<=R) return sum[root]; 36 int mid=l+r>>1; 37 return find(root*2,l,mid)+find(root*2+1,mid+1,r); 38 } 39 40 //求最大子段和问题 41 int L[100001],R[100001],ans[100001]; //L表示前缀和,R表示后缀和,ans表示答案 42 void change(int root){ 43 sum[root]=sum[root*2]+sum[root*2+1]; 44 45 L[root]=max(L[root*2],sum[root*2]+L[root*2+1]); 46 R[root]=max(R[root*2+1],sum[root*2+1]+R[root*2]); 47 48 ans[root]=max(max(ans[root*2],ans[root*2+1]),R[root*2]+L[root*2+1]); 49 }//ans即为解 find中把sum改为ans即可 50 51 //区间修改,区间加,打懒标记 52 //区间加 区间和 53 [L,R] +x 54 int A[410000],sum[410000]; 55 void pushdown(int k1){ //把懒标记带下去 56 A[k1*2]+=A[k1]; A[k1*2+1]+=A[k1]; A[k1]=0; 57 } 58 void addall(int k1,int l,int r,int L,int R){ 59 if (l>R||r<L) return; 60 if (L<=l&&R>=r){ 61 sum[k1]+=x*(r-l+1); //快速更新 62 A[k1]+=x; return; 63 } 64 int mid=l+r>>1; pushdown(k1); 65 addall(k1*2,l,mid,L,R); 66 addall(k1*2+1,mid+1,r,L,R); 67 change(k1); 68 } 69 int find(int k1,int l,int r,int L,int R){ 70 if (l>R||r<L) return 0; 71 if (L<=l&&R>=r) return sum[k1]; 72 int mid=l+r>>1; pushdown(k1); 73 return find(k1*2,l,mid,L,R)+find(k1*2+1,mid+1,r,L,R); 74 } 75 76 //有懒标记的线段树 77 78 //1.change() 79 //2.pushdown() -> 两个标记的合并 +5 +6 => +11 80 //3.再给一个节点打上懒标记的时候需要快速更新维护的信息
int a[100001],sum[100001]; //sum表示线段树,空间O(2n),a表示序列
//建立一颗线段树 void change(int root){sum[root]=sum[root*2]+sum[root*2+1];}void buildtree(int root,int l,int r){if (l==r) {sum[root]=a[l]; return;}int mid= l+r>>1;buildtree(root*2,l,mid);buildtree(root*2+1,mid+1,r);change(root);}
//单点修改qvoid change(int root){sum[root]=sum[root*2]+sum[root*2+1];}void change (int root,int l,int r,int p,int q){if (l==r) {sum[root]+=q; return;}int mid=l+r>>1;if (p<=mid) change(root*2,l,mid,p,q);else change(root*2+1,mid+1,r,p,q);change(root);}
//求区间和[L,R]int find(int root,int l,int r,int L,int R){if (r<L||l>R) return 0;if (l>=L&&r<=R) return sum[root];int mid=l+r>>1;return find(root*2,l,mid)+find(root*2+1,mid+1,r);}
//求最大子段和问题int L[100001],R[100001],ans[100001]; //L表示前缀和,R表示后缀和,ans表示答案 void change(int root){sum[root]=sum[root*2]+sum[root*2+1];L[root]=max(L[root*2],sum[root*2]+L[root*2+1]);R[root]=max(R[root*2+1],sum[root*2+1]+R[root*2]);ans[root]=max(max(ans[root*2],ans[root*2+1]),R[root*2]+L[root*2+1]);}//ans即为解 find中把sum改为ans即可 //区间修改,区间加,打懒标记//区间加 区间和 [L,R] +xint A[410000],sum[410000];void pushdown(int k1){ //把懒标记带下去 A[k1*2]+=A[k1]; A[k1*2+1]+=A[k1]; A[k1]=0;}void addall(int k1,int l,int r,int L,int R){if (l>R||r<L) return;if (L<=l&&R>=r){sum[k1]+=x*(r-l+1); //快速更新 A[k1]+=x; return;}int mid=l+r>>1; pushdown(k1);addall(k1*2,l,mid,L,R);addall(k1*2+1,mid+1,r,L,R);change(k1);}int find(int k1,int l,int r,int L,int R){if (l>R||r<L) return 0;if (L<=l&&R>=r) return sum[k1];int mid=l+r>>1; pushdown(k1);return find(k1*2,l,mid,L,R)+find(k1*2+1,mid+1,r,L,R);}
//有懒标记的线段树
//1.change()//2.pushdown() -> 两个标记的合并 +5 +6 => +11//3.再给一个节点打上懒标记的时候需要快速更新维护的信息