线段树一:修改点的值求任意区间的值
来源:互联网 发布:rime输入法linux 编辑:程序博客网 时间:2024/05/12 22:07
强烈推荐:这篇文章------------------------(完全版)线段树。
线段树的作用:解决区间计算问题。
例如:记录一个区间的最值(最大或最小值)和总量,并在区间的插入、删除、修改中维护这些最值和总量。
线段树是一颗二叉树。记为T(a,b),a,b表示顶点T为区间[a,b]。区间长度b-a记为L。递归定义T[a,b]:
若区间长度L>=1,区间[a,(a+b)/2]为T的左孩子,区间((a+b)/2,b]为T的右孩子。
如图为区间[1,10]的线段树:
线段树的基本操作:
(1)建立线段树T(a,b)。
(2)将区间[c,d]插入到线段树T(a,b)。
(3)将区间[c,d]从线段树T(a,b)中删除。
(4)对线段树进行动态维护。
以下代码转载自:himdd的文章。
线段树的建立:
int Pushup(int root) //把当前结点的信息更新到父节点 { sum[root]=sum[root<<1]+sum[root<<1|1];}void Build(int L,int R,int root){ if(L==R) { scanf("%d",&sum[root]); return ; } int mid=(L+R)>>1; Build(Lson); //左孩子 Build(Rson); //右孩子 Pushup(root); }
上面代码构造的树为,我没有画完整,但是已经能够体现问题了。
后面的数相当与这颗树的根,区间[1,10]的根设为1,所以[1,5]的就为1<<1=2,[6,10]的根为:(1<<1)+1=3,依次类推,然后用sum[root]表示根节点为root的所代表的这个区间的值,如:sum[1]保存的就是[1,10]这个区间的值。所以有:sum[1]=sum[2]+sum[3],即:sum[root]=sum[root>>1]+sum[(root>>1)+1]。
线段树的修改:比方说我要修改某一个结点的值,那么我只需要沿着根结点1往下寻找,并记录这条路径,更新sum的值即可,比方说变化2:它需要修改的区间依次为:[1,10]
->[1,5]->[1,3]->[1,2]->[2,2]。具体操作见代码:
void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val { if(L==R) { sum[root]+=val; return ; } int mid=(L+R)>>1; if(q<=mid) Update(q,val,Lson); //说明p在左结点 else Update(q,val,Rson); //说明p在右结点 Pushup(root);}
至于统计某一区间[a,b]的值,那么同样可以利用二分的思想:
比方说要求[4,8],二分mid=(4+8)/2=6,然后它就可以写成:[4,6]+[7,8]=[4,5]+[6,6]+[7,7]+[8,8]=[4,4]+[5,5]+...+[8,8]。代码见下题中的Query部分。
HDU 1166(敌兵布阵),相当于树状数组中的插点问线,初始化给你每个结点的值,然后更新每个点的值,求给定的任意一个区间的值。
#include<iostream>#include<cstring>#include<cstdio> using namespace std;const int MAX=50010;#define Lson L,mid,root<<1 //遇到Lson的时候强制替换为后面的语句 #define Rson mid+1,R,root<<1|1int n,sum[MAX<<2];int Pushup(int root) //把当前结点的信息更新到父节点 { sum[root]=sum[root<<1]+sum[root<<1|1];}void Build(int L,int R,int root){ if(L==R) { scanf("%d",&sum[root]); return ; } int mid=(L+R)>>1; Build(Lson); //左孩子 Build(Rson); //右孩子 Pushup(root); }void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val { if(L==R) { sum[root]+=val; return ; } int mid=(L+R)>>1; if(q<=mid) Update(q,val,Lson); //说明p在左结点 else Update(q,val,Rson); //说明p在右结点 Pushup(root);}int Query(int ql,int qr,int L,int R,int root) //在根为root,区间为[L,R]的线段树中,计算区间[ql,qr]的和 { if(ql<=L && R<=qr) return sum[root]; int mid=(L+R)>>1; //利用二分的思想 int res=0; if(ql<=mid) res+=Query(ql,qr,Lson); if(qr>mid) res+=Query(ql,qr,Rson); return res;}int main(){ int a,b,Case,num=1; scanf("%d",&Case); while(Case--) { printf("Case %d:\n",num++); scanf("%d",&n); Build(1,n,1); char op[10]; while(scanf("%s",op)) { if(op[0]=='E') break; scanf("%d%d",&a,&b); if(op[0]=='A') Update(a,b,1,n,1); if(op[0]=='S') Update(a,-b,1,n,1); if(op[0]=='Q') printf("%d\n",Query(a,b,1,n,1)); } } return 0;}
- 线段树一:修改点的值求任意区间的值
- 线段树一:修改点的值求任意区间的值
- 线段树二:修改区间的值求任意点的值
- 线段树三:求任意区间的最值
- UESTC 1425 求任意区间的LIS 线段树区间更新区间查询
- zoj3911 线段树区间修改与求素数的结合
- 线段树的区间修改
- POJ2823 - 线段树求区间的最值..
- 线段树模板:点修改,区间修改
- hdu1698--Just a Hook(线段树修改区间值,求区间和)
- 线段树 --- 适用于修改性的求区间问题 【单点修改】
- 划分树求一个数列任意区间的第k小值
- 线段树3种基础模型的理解和记忆(任意区间求和,任意区间的所有数加上相同数(懒操作),任意区间所有数变成同一个值再求和)
- poj 2892 hdu1540 线段树 求过某点的最大连续区间。
- [线段树] [求区间最大] [定点修改]
- hihoCoder 1078 线段树的区间修改
- hihoCoder 1078 : 线段树的区间修改
- hiho1078 线段树的区间修改
- win平台改代码到android 平台需要注意
- 读指定时间的视频
- VS2010快捷键
- Store获取json字符串
- socket编程 详解
- 线段树一:修改点的值求任意区间的值
- 做手机应用,免费反而比收费更挣钱?
- 内部排序总结(四)归并排序
- 设计模式之装饰者模式
- ASP.NET Ajax编程技术学习
- jbpm5入门
- Wine乱码问题解决方案
- EditPuls保存时不生成.bak文件
- 使用Xmanager在CentOs5.5 安装oracle11g r2