线段树入门(二)
上一讲,我们学会了线段树,于是我们就愉(hua)快(ji)地可以暴(da)虐(lian)各大OJ的线段树模板题了。
于是我们就很愉快的打开了某谷oj的模板题。。。
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
看到这道题我们便很愉快的打上了线段树,一个一个点更新,线段树区间求和,于是便很愉快的TLE了。
你没有看错,线段树这么高效(搞笑)的更新和查询都会TLE。
TLE的原因在于,我们对区间进行区间更新时,使用单点更新,每次复杂度为O(log n),每次最多有n个点要更新,一共有m个操作,总复杂度就为O(m*n*log n),这样肯定会超时的。
怎样解决这个问题呢?我们可以使用延迟标记的思想就可以很好的解决这个问题。
延迟标记:每个节点新增加一个标记,记录这个节点是否进行了某种修改(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。
这样做的优点在于,不用将区间内的所有值都暴力更新,大大提高效率,因此区间更新是最有用的操作
struct segtree { int l,r; long long sum,lazy;//lazy表示延迟标记 void update(long long x) { sum += (r-l+1)*x;//修改节点的信息 lazy += x;//标记上这种操作的标记 } }tree[maxn<<2];
void push_down(int x)//这个函数用于将标记往下传递 { int lazyval = tree[x].lazy;//获取延迟标记 if(lazyval) { tree[x<<1].update(lazyval);//把标记分别传递给左子节点和右子节点 tree[x<<1|1].update(lazyval); tree[x].lazy = 0;//清空延迟标记 } }
知道了这两个函数以后我们就可以继续往下学习更新和查询操作了,由于建树的操作一样就不予复述了。
更新操作其实和单点更新差不多,只是多加了一个延迟标记而已。
void update(int x,int l,int r,long long val) { int L=tree[x].l,R=tree[x].r; if(l<=L&&R<=r)//当前区间全部在更新区间内 tree[x].update(val);//直接更新 else { push_down(x);//将lazy标记往下传 int mid = (L+R)>>1; if(mid>=l) update(x<<1,l,r,val);//跟单点更新一样更新左右子节点 if(r>mid) update(x<<1|1,l,r,val); push_up(x);//自下往上维护线段树 } }
查询操作和更新操作基本一样。。。
long long query(int x,int l,int r)//用long long防爆int { int L=tree[x].l,R=tree[x].r; if(l<=L&&R<=r)//当前区间全在查询区间内 return tree[x].sum; else { push_down(x);//传递lazy标记 long long ans = 0; int mid = (L+R)>>1; if(mid>=l) ans += query(x<<1,l,r);//查询左右子节点 if(r>mid) ans += query(x<<1|1,l,r); push_up(x);//自下往上维护线段树 return ans;//返回 } }
这样我们就真正可以暴虐模板题了。
最后附上模板题完整代码。。。
#include<bits/stdc++.h> using namespace std; const int maxn = 100000 +10; struct segtree { int l,r; long long sum,lazy; void update(long long x) { sum += (r-l+1)*x; lazy += x; } }tree[maxn<<2]; int a[maxn],n,m; void push_up(int x) { tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum; } void push_down(int x) { int lazyval = tree[x].lazy; if(lazyval) { tree[x<<1].update(lazyval); tree[x<<1|1].update(lazyval); tree[x].lazy = 0; } } void build(int root,int l,int r) { tree[root].l = l;tree[root].r = r; if(l==r) tree[root].sum = a[l]; else { int mid = (l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); push_up(root); } } void update(int x,int l,int r,long long val) { int L=tree[x].l,R=tree[x].r; if(l<=L&&R<=r) tree[x].update(val); else { push_down(x); int mid = (L+R)>>1; if(mid>=l) update(x<<1,l,r,val); if(r>mid) update(x<<1|1,l,r,val); push_up(x); } } long long query(int x,int l,int r) { int L=tree[x].l,R=tree[x].r; if(l<=L&&R<=r) return tree[x].sum; else { push_down(x); long long ans = 0; int mid = (L+R)>>1; if(mid>=l) ans += query(x<<1,l,r); if(r>mid) ans += query(x<<1|1,l,r); push_up(x); return ans; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); build(1,1,n); while(m--) { int c,x,y,k; scanf("%d%d%d",&c,&x,&y); if(c==1) { scanf("%d",&k); update(1,x,y,k); } else printf("%lld\n",query(1,x,y)); } return 0; }