线段树在n较小、操作较多的情况下效率很高。虽然如此,如果直接暴力进行修改的话还是会TLE得很惨。于是一个叫做懒标记的东西应运而生。
懒标记
why优秀:在修改一个节点时,若此点已经被懒标记所标记,我们就将此点懒标记取消,标记左右子节点传递下去,当被更新或者被查询时再更新节点,节约了根本不会被用到的花费。这也是线段树作为“树”优秀的地方。
开始学习
洛谷已经为我们排好了对线段树的逐层深度学习XD
P3372 【模板】线段树 1
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=1e5,maxm=1e5; 5 typedef long long ll; 6 void read(int &x) 7 { 8 char c=getchar(); 9 while(c<'0' or c>'9')c=getchar(); 10 x=c-'0',c=getchar(); 11 while(c>='0' and c<='9'){ 12 x*=10,x+=c-'0',c=getchar(); 13 } 14 } 15 int a[maxn*10]; 16 struct node{ 17 ll l,r,sum,tag; 18 }e[maxn*6]; 19 int n,m,k; 20 inline ll ls(ll ro){return ro<<1;} 21 inline ll rs(ll ro){return ro<<1|1;} 22 inline void push_up(ll ro) 23 { 24 e[ro].sum=e[ls(ro)].sum+e[rs(ro)].sum; 25 } 26 inline void push_down(ll ro) 27 { 28 e[ls(ro)].sum+=e[ro].tag*(e[ls(ro)].r-e[ls(ro)].l+1); 29 e[rs(ro)].sum+=e[ro].tag*(e[rs(ro)].r-e[rs(ro)].l+1); 30 e[ls(ro)].tag+=e[ro].tag; 31 e[rs(ro)].tag+=e[ro].tag; 32 e[ro].tag=0; 33 } 34 35 void build(ll ro,ll l,ll r) 36 { 37 e[ro].l=l,e[ro].r=r; 38 if(l==r) 39 { 40 e[ro].sum=a[l]; 41 return; 42 } 43 e[ro].tag=0; 44 ll mid=(l+r)>>1; 45 build(ls(ro),l,mid); 46 build(rs(ro),mid+1,r); 47 push_up(ro); 48 } 49 50 void update(ll ro,ll l,ll r) 51 { 52 if(e[ro].l>=l and r>=e[ro].r) 53 { 54 e[ro].sum+=k*(e[ro].r-e[ro].l+1); 55 e[ro].tag+=k; 56 return; 57 } 58 push_down(ro); 59 ll mid=(e[ro].l+e[ro].r)>>1; 60 if(l<=mid)update(ls(ro),l,r); 61 if(r> mid)update(rs(ro),l,r); 62 push_up(ro); 63 } 64 65 ll check(ll ro,ll l,ll r) 66 { 67 if(l<=e[ro].l and r>=e[ro].r)return e[ro].sum; 68 push_down(ro); 69 int mid=(e[ro].l+e[ro].r)>>1; 70 ll ans=0; 71 if(l<=mid)ans+=check(ls(ro),l,r); 72 if(r> mid)ans+=check(rs(ro),l,r); 73 return ans; 74 } 75 signed main() 76 { 77 read(n);read(m); 78 for(int i=1;i<=n;i++) 79 read(a[i]); 80 build(1,1,n); 81 int casee,l,r; 82 for(int i=1;i<=m;i++) 83 { 84 read(casee); 85 switch(casee) 86 { 87 case(1):{ 88 read(l);read(r);read(k); 89 update(1,l,r); 90 break; 91 } 92 case(2):{ 93 read(l);read(r); 94 printf("%lld\n",check(1,l,r)); 95 break; 96 } 97 } 98 } 99 return 0; 100 }
P3373 【模板】线段树 2
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=1e5; 5 typedef long long ll; 6 int n,m,p,a[maxn],k; 7 struct tree{ 8 ll l,r,val,add,tag; 9 }e[maxn*4]; 10 inline void read(int &x) 11 { 12 char c=getchar(); 13 while(c<'0' or c>'9')c=getchar(); 14 x=c-'0',c=getchar(); 15 while(c>='0' and c<='9')x*=10,x+=c-'0',c=getchar(); 16 } //这个快读读不了负数 XD 17 inline ll ls(int ro){return ro<<1;} 18 inline ll rs(int ro){return ro<<1|1;} 19 20 inline void lazy_add(ll ro) 21 { 22 e[ls(ro)].add=(e[ls(ro)].add*e[ro].tag+e[ro].add)%p; 23 e[rs(ro)].add=(e[rs(ro)].add*e[ro].tag+e[ro].add)%p; 24 //给子节点加上懒标记,add需要乘tag才能保证不变,再加上add 25 } 26 27 inline void lazy_tag(ll ro) 28 { 29 e[ls(ro)].tag=(e[ls(ro)].tag*e[ro].tag)%p; 30 e[rs(ro)].tag=(e[rs(ro)].tag*e[ro].tag)%p; 31 //相同,乘标记挺正常 32 } 33 34 inline void push_down(ll ro) 35 { 36 e[ls(ro)].val=(e[ls(ro)].val*e[ro].tag+e[ro].add*(e[ls(ro)].r-e[ls(ro)].l+1))%p; 37 e[rs(ro)].val=(e[rs(ro)].val*e[ro].tag+e[ro].add*(e[rs(ro)].r-e[rs(ro)].l+1))%p; 38 //分析:给子节点的值乘上乘标记值,再加上加标记在它区间元素个数的和; 39 lazy_tag(ro); 40 lazy_add(ro); 41 e[ro].add=0,e[ro].tag=1;//别忘了清除懒标记 42 } 43 44 inline void push_up(ll ro) 45 { 46 e[ro].val=(e[ls(ro)].val+e[rs(ro)].val)%p;//给爷加和 因为这个题只需要输出和所以这样做,并不普遍 47 } 48 49 void build(ll ro,ll l ,ll r) 50 { 51 e[ro].l=l,e[ro].r=r; 52 e[ro].tag=1,e[ro].add=0; 53 if(l==r){e[ro].val=a[l];return;}//赋值 54 int mid=(l+r)>>1; 55 build(ls(ro),l,mid); 56 build(rs(ro),mid+1,r); 57 push_up(ro); 58 e[ro].val%=p;//膜一下 59 } 60 61 //更新乘 62 void update_tag(ll ro,ll l ,ll r) 63 { 64 if(e[ro].l>=l and e[ro].r<=r) 65 { 66 e[ro].val=(e[ro].val*k)%p; 67 e[ro].tag=(e[ro].tag*k)%p; 68 e[ro].add=(e[ro].add*k)%p; 69 return ; 70 } 71 push_down(ro); 72 int mid=(e[ro].l+e[ro].r)>>1; 73 if(l<=mid)update_tag(ls(ro),l,r); 74 if(r> mid)update_tag(rs(ro),l,r); 75 push_up(ro); 76 } 77 78 //更新和 79 void update_add(ll ro,ll l,ll r) 80 { 81 if(e[ro].l>=l and e[ro].r<=r) 82 { 83 e[ro].add=(e[ro].add+k)%p; 84 e[ro].val=(e[ro].val+k*(e[ro].r-e[ro].l+1))%p;//注意这个是改区间和 85 return ; 86 } 87 push_down(ro); 88 int mid=(e[ro].l+e[ro].r)>>1; 89 if(l<=mid)update_add(ls(ro),l,r); 90 if(r> mid)update_add(rs(ro),l,r); 91 push_up(ro); 92 } 93 94 ll query(ll ro,ll l,ll r) 95 { 96 if(e[ro].l>=l and e[ro].r<=r) 97 return e[ro].val; 98 push_down(ro); 99 int mid=(e[ro].l+e[ro].r)>>1; 100 int ans=0; 101 if(l<=mid) ans+=query(ls(ro),l,r); 102 if(r> mid) ans+=query(rs(ro),l,r); 103 return ans%p; 104 } 105 int main() 106 { 107 read(n);read(m);read(p); 108 for(int i=1;i<=n;i++)read(a[i]); 109 build(1,1,n); 110 int casee,l,r; 111 while(m--) 112 { 113 read(casee); 114 switch(casee) 115 { 116 case(1):{ 117 read(l);read(r);read(k); 118 update_tag(1,l,r); 119 break; 120 } 121 case(2):{ 122 read(l);read(r);read(k); 123 update_add(1,l,r); 124 break; 125 } 126 case(3):{ 127 read(l);read(r); 128 printf("%lld\n",query(1,l,r)); 129 break; 130 } 131 } 132 } 133 return 0; 134 }
P6242 【模板】线段树 3
我不会XD
有待更新
(其实和2一样就是复杂一点)