线段树
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。
线段树,顾名思义,是一种可以以log2n的时间复杂度来进行区间访问和区间查询求和的骚包操作,不同于一般的N或者是N2的算法,特点就是快,由于二叉树的性质,所以可以用位运算优化的一种裸的基础的简单数据结构。由于二叉树的性质,兴许是满的?总之左儿子是其父亲的两倍,右儿子是其父亲的两倍加一,所以更新父亲的区间操作可以写成:tree[o] = tree[o<<1] + tree[o<<1|1];
那么首先,有两种操作,一种是在建树的时候,当到叶子节点的时候再读入,或者是直接读入到arr[i]中,当建树建到叶子节点的时候,把arr[o]赋值给当前tree[o]。那么怎么建树呢?
void pushup(int o){tree[o]=tree[o<<1]+tree[o<<1|1];} void build(int o,int L,int R) { if(L==R){ tree[o]=arr[L];//也可以写成: scanf("%d",&tree[o]); return ; } int M = (L+R)>>1; build(int o<<1,L,M); build(int o<<1|1,M+1,R); pushup(o); }
因为建树(build)过程中只更新了儿子节点,所以要有一个pushup操作,目的就是为了更新祖先节点的值。
那么就可以分段处理了,比如如果要单点修改就要用update了
单点修改:
void update(int o,int L,int R,int x,int val) { if(L==R){//找到了要修改的值 tree[o]+=val; return ; } int M = (L+R)<<1; if(x>M) update(o<<1|1,M+1,R,x,val);//如果要修改的点在左右端点的右边的话,那就是要更新左端点的值,并且当前标号o也要做相应的变换 else update(o<<1,L,M,x,val); pushup(o);//因为叶子节点的值有更新,所以祖先节点还是有改变。 }
区间求和:
int query(int o,int L,int R,int ql,int qr) { if(ql>=L&&R>=qr) return tree[o];//找到叶子节点,把叶子节点的值加入所求的ret中。 int M = (L+R)>>1; int ret = 0 ; if(ql >= M) ret += query(o<<1,L,M,ql,qr);//往左边找 if(ql < M) ret += query(o<<1|1,M+1,R,ql,qr);//往右 return ret ;//返回和 }
区间修改:
void pushdown(int o,int L,int R) { if(L==R) { tree[o] += lazy[o];//到达叶子节点,没法分配任务就把总值更新了 return ; } lazy[o<<1] += lazy[o];//把任务分配给自己的左右儿子 lazy[o<<1|1] += lazy[o]; lazy[o] = 0 ;//任务分配完自己清零 } void update(int o,int L,int R,int ql ,int qr,int val) { if(L==R){ tree[o] += val ; return ; } pushdown(o,L,R);// 先往下传标记 int M=(L+R)>>1; if(ql<=M) update(o<<1,L,M,ql,qr,val);//改左边 if(qr>M) update(o<<1|1,M+1,R,ql,qr,val);//改右边 return ; }
最后一个,区间求和+区间修改,在用正常区间修改的方法,也就是用下传标志的方法求和,与一般的区间求和不同的是,pushup改变了,其父亲tree[o]+=tree[o<<1]*lazy[o<<1]*(M-L+1)+tree[o<<1|1]*lazy[o<<1|1]*(R-M)
那么就可以上codevs上做题啦
HDU 上也有很多题可以做啦
HDU 1394 Minimum Inversion Number
HDU 4027 Can you answer these queries?
那就祝大家暑假玩的开心?
7.15清北见。
还有93天初赛, 还有121天复赛。
那是我愿意付诸一生的人,往日不再重来。
以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章