线段树
Posted lovedsr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。
打了一晚上,先存一下,明天再改,嘿嘿
线段树码,比树状数组好理解多了(其实理解了,都挺简单的)
就是代码贼长
//支持操作 //
建树 Build(1,n,1);、
//把1到n的a[]建一颗线段树
//点更新 Add(L,C,1,n,1);//L改成C
//区间修改 Update(L,R,C,1,n,1);
//L到R的区间加上C
//区间查询 int ANS=Query(L,R,1,n,1); //L到R的区间和
//支持操作 //建树 Build(1,n,1);//把1到n的a[]建一颗线段树 //点更新 Add(L,C,1,n,1);//L改成C //区间修改 Update(L,R,C,1,n,1);//L到R的区间加上C //区间查询 int ANS=Query(L,R,1,n,1); //L到R的区间和 const int MAXN=50010; int a[MAXN],ans[MAXN<<2],lazy[MAXN<<2]; //a[]为原序列信息,ans[]模拟线段树维护区间和,lazy[]为懒惰标记 void PushUp(int rt) { //更新节点信息 ans[rt]=ans[rt<<1]+ans[rt<<1|1]; }//rt<<1表示左孩子,rt<<1|1表示右孩子 //如果不理解可以改成rt*2和rt*2+1 //st<<1是二进制码左移1,二进制,所以就是乘2(2^1),相似的<<2,是乘2^2,以此类推 //st<<1|1,st<<1之后肯定是偶数,毫无疑问二进制的第一位是0,然后|1就是加1 void Build(int l,int r,int rt) { //递归建树,从l到r,rt是数组的标号 if (l==r) { ans[rt]=a[l]; return; }//递归终点 int mid=(l+r)>>1;//这是一棵树,分成两半 Build(l,mid,rt<<1);//左子树 Build(mid+1,r,rt<<1|1);//右子树 PushUp(rt); //更新ans[rt] } void PushDown(int rt,int ln,int rn) { //ln表示左子树元素结点个数,rn表示右子树结点个数 /////////////////////////////////表示不会,不懂 if (lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; ans[rt<<1]+=lazy[rt]*ln; ans[rt<<1|1]+=lazy[rt]*rn; lazy[rt]=0; } } void Add(int L,int C,int l,int r,int rt) { //点更新 //把L号改成C,在l到r的区间内查询,这玩意貌似就是二分找值 if (l==r) { //重合了,就找到 ans[rt]+=C; return; } int mid=(l+r)>>1;//中间值 PushDown(rt,mid-l+1,r-mid); //若既有点更新又有区间更新,需要这句话!!!dont know if (L<=mid)//如果这个点小于mid,他肯定在左子树里 Add(L,C,l,mid,rt<<1); else//否则,他肯定在右子树里 Add(L,C,mid+1,r,rt<<1|1); PushUp(rt);//更新节点信息 } void Update(int L,int R,int C,int l,int r,int rt) { //区间修改 // if (L<=l&&r<=R) { ans[rt]+=C*(r-l+1);//改变ans[]的值 lazy[rt]+=C;//懒惰标记+C,不必再往下更改 return; }//递归终点 int mid=(l+r)>>1; PushDown(rt,mid-l+1,r-mid); if (L<=mid) Update(L,R,C,l,mid,rt<<1);//如果L都大于mid,那肯定都在mid的 if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1); PushUp(rt);//更新 } LL Query(int L,int R,int l,int r,int rt) { //区间查询 //L到R的和,在l和r的里面找,rt是树的标号(在哪个数下) if (L<=l&&r<=R)//如果标号正好重叠了 return ans[rt];//那结果就是我们事先算好的ans int mid=(l+r)>>1;//不能求出,就分开考虑 PushDown(rt,mid-l+1,r-mid);//若更新只有点更新,不需要这句!!!不会这句话 LL ANS=0; if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1);//左孩子的值 if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1);//右孩子的值 return ANS;//加起来的值返回 }
以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章