线段树小结
线段树
作用
适合具备结合律的运算(sum,xor,max,min……)
像归并排序一样,把所有数据反复切一半
那么叶子节点即为原数的自身区间 exam:[i,i]
按照BFS的顺序将这棵树编号(外层为层数,内层为同意层的序号)
最后一层节点可能不是连续的,直接空出来即可。
可以发现,每个非叶子节点的左儿子的编号是其两倍,右儿子的编号是其两倍加一
保存线段树的数组(struct)长度不能小于4*N
void build(int now,int l,int r)
- 更新l,r
- 判断是否为叶子节点(l==r)
- int mid=(l+r)>>2//卡常省时
- build左区间&&右区间
- 回溯后(不用返回值)将数据代入递推式
递推式:(以max为例)
dat[l,r]=max(dat[l,mid],dat[mid,r])
建立线段树以后,每个节点上的信息就为其对应区间的最大值
模板题
Code
1 #include<iostream> 2 #include<cstring> 3 #define LL long long 4 using namespace std; 5 struct Edge{ 6 LL l,r,sum,add; 7 #define l(x) tree[x].l 8 #define r(x) tree[x].r 9 #define sum(x) tree[x].sum 10 #define add(x) tree[x].add 11 }tree[400010]; 12 LL n,m; 13 LL ans[100010]; 14 void build(int l,int r,int p) 15 { 16 l(p)=l,r(p)=r; 17 if(l==r){sum(p)=ans[l];return;} 18 LL mid=(l+r)/2; 19 build(l,mid,p*2); 20 build(mid+1,r,p*2+1); 21 sum(p)=sum(p*2)+sum(p*2+1); 22 } 23 void spread(LL p) 24 { 25 if(add(p)) 26 { 27 sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1); 28 sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1); 29 add(p*2)+=add(p); 30 add(p*2+1)+=add(p); 31 add(p)=0; 32 } 33 } 34 void change(LL l,LL r,LL p,LL k) 35 { 36 if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;} 37 LL mid=(l(p)+r(p))/2; 38 spread(p); 39 if(l<=mid) change(l,r,p*2,k); 40 if(r>=mid+1) change(l,r,p*2+1,k); 41 sum(p)=sum(p*2)+sum(p*2+1); 42 } 43 LL ask(LL l,LL r,LL p) 44 { 45 if(l<=l(p)&&r>=r(p)) return sum(p); 46 spread(p); 47 LL mid=(l(p)+r(p))/2,val=0; 48 if(l<=mid) val+=ask(l,r,p*2); 49 if(r>=mid+1) val+=ask(l,r,p*2+1); 50 return val; 51 } 52 int main() 53 { 54 cin>>n>>m; 55 for(int i=1;i<=n;i++) cin>>ans[i]; 56 build(1,n,1); 57 int t,a,b,c; 58 while(m--) 59 { 60 cin>>t; 61 if(t==1){ 62 cin>>a>>b>>c; 63 change(a,b,1,c); 64 } 65 if(t==2){ 66 cin>>a>>b; 67 cout<<ask(a,b,1)<<endl; 68 } 69 } 70 }
例题2
Code
1 #include<iostream> 2 #include<cstring> 3 #define LL long long 4 using namespace std; 5 struct Edge{ 6 LL l,r,sum,add; 7 #define l(x) tree[x].l 8 #define r(x) tree[x].r 9 #define sum(x) tree[x].sum 10 #define add(x) tree[x].add 11 }tree[800010]; 12 LL n,m; 13 LL ans[200010]; 14 void build(int l,int r,int p) 15 { 16 l(p)=l,r(p)=r; 17 if(l==r){sum(p)=ans[l];return;} 18 LL mid=(l+r)/2; 19 build(l,mid,p*2); 20 build(mid+1,r,p*2+1); 21 sum(p)=sum(p*2)+sum(p*2+1); 22 } 23 void spread(LL p) 24 { 25 if(add(p)) 26 { 27 sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1); 28 sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1); 29 add(p*2)+=add(p); 30 add(p*2+1)+=add(p); 31 add(p)=0; 32 } 33 } 34 void change(LL l,LL r,LL p,LL k) 35 { 36 if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;} 37 LL mid=(l(p)+r(p))/2; 38 spread(p); 39 if(l<=mid) change(l,r,p*2,k); 40 if(r>=mid+1) change(l,r,p*2+1,k); 41 sum(p)=sum(p*2)+sum(p*2+1); 42 } 43 LL ask(LL l,LL r,LL p) 44 { 45 if(l<=l(p)&&r>=r(p)) return sum(p); 46 spread(p); 47 LL mid=(l(p)+r(p))/2,val=0; 48 if(l<=mid) val+=ask(l,r,p*2); 49 if(r>=mid+1) val+=ask(l,r,p*2+1); 50 return val; 51 } 52 int main() 53 { 54 cin>>n>>m; 55 for(int i=1;i<=n;i++) cin>>ans[i]; 56 build(1,n,1); 57 int t,a,b,c; 58 while(m--) 59 { 60 cin>>t; 61 if(t==1){ 62 cin>>a>>b>>c; 63 change(a,b,1,c); 64 } 65 if(t==2){ 66 cin>>c; 67 change(1,1,1,c); 68 } 69 if(t==3){ 70 cin>>c; 71 change(1,1,1,-c); 72 } 73 if(t==4){ 74 cin>>a>>b; 75 cout<<ask(a,b,1)<<endl; 76 } 77 if(t==5){ 78 cout<<ask(1,1,1)<<endl; 79 } 80 } 81 }
卡常
很明显,这玩意可以卡常
以模板题为例
把p*2换成p<<1
把p*2+1换成p<<1|1
把(l+r)/2换成l+r>>1
但是不能写p<<1|1,否则会使1+1先计算
Code
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 struct Edge{ 7 LL l,r,sum,add; 8 #define l(x) tree[x].l 9 #define r(x) tree[x].r 10 #define sum(x) tree[x].sum 11 #define add(x) tree[x].add 12 }tree[400010]; 13 LL n,m; 14 LL ans[100010]; 15 void build(LL l,LL r,LL p) 16 { 17 l(p)=l,r(p)=r; 18 if(l==r){sum(p)=ans[l];return;} 19 LL mid=(l+r)>>1; 20 build(l,mid,p<<1); 21 build(mid+1,r,p<<1|1); 22 sum(p)=sum(p<<1)+sum(p<<1|1); 23 } 24 void spread(LL p) 25 { 26 if(add(p)) 27 { 28 sum(p<<1)+=add(p)*(r(p<<1)-l(p<<1)+1); 29 sum(p<<1|1)+=add(p)*(r(p<<1|1)-l(p<<1|1)+1); 30 add(p<<1)+=add(p); 31 add(p<<1|1)+=add(p); 32 add(p)=0; 33 } 34 } 35 void change(LL l,LL r,LL p,LL k) 36 { 37 if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;} 38 LL mid=l(p)+r(p)>>1; 39 spread(p); 40 if(l<=mid) change(l,r,p<<1,k); 41 if(r>mid) change(l,r,p<<1|1,k); 42 sum(p)=sum(p<<1)+sum(p<<1|1); 43 } 44 LL ask(LL l,LL r,LL p) 45 { 46 if(l<=l(p)&&r>=r(p)) return sum(p); 47 spread(p); 48 LL mid=l(p)+r(p)>>1,val=0; 49 if(l<=mid) val+=ask(l,r,p<<1); 50 if(r>mid) val+=ask(l,r,p<<1|1); 51 return val; 52 } 53 int main() 54 { 55 scanf("%lld%lld",&n,&m); 56 for(int i=1;i<=n;i++) scanf("%lld",ans+i); 57 build(1,n,1); 58 LL t,a,b,c; 59 while(m--) 60 { 61 cin>>t; 62 if(t==1){ 63 scanf("%lld%lld%lld",&a,&b,&c); 64 change(a,b,1,c); 65 } 66 if(t==2){ 67 scanf("%lld%lld",&a,&b); 68 printf("%lld\n",ask(a,b,1)); 69 } 70 } 71 }
小结
理论知识始终不实践==没有知识
纸上得来终觉浅,绝知此事要躬行。