线段树入门学习(兼解HDU1754) - 心中的利剑:HTML5 Canvas游戏动画(JavaScript) - ITeye技术网站
线段树也叫区间树,顾名思义,线段树是一种基于区间的树,每个节点表示一个"线段"或"区间"。树的根节点表示是"整体"的区间,左右子树分别表示这个区间的左半边和右半边。
假设要构造一个表示N个区间大小的线段树,线段树的根节点表示区间[0,N-1],然后将区间分成两半,分别为左右子树表示,这样的线段树的节点只有2N-1个,是O(N)级别的,如图所示表示构造区间[1-5]的线段树。
线段树可以使用链状结构动态构造,这样的优点是节省空间,空间复杂度在2N,但是缺点就是编程复杂度大,执行效率低;
所以线段树一般是使用一维数组以完全二叉树的方式来存储,类似堆,这样的编程复杂度低,执行效率高,但是耗费空间,一般来讲,这样情况下都是申请4N的空间.
例: HDU1754
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
Output
对于每一次询问操作,在一行里面输出最高成绩。
Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
Sample Output
5
6
5
9
在那里用链状结构实现了二叉线段树,下面程序使用一维数组以完全二叉树的方式来存储。
POJ3468
大致题意:给出一个长度为n的整数序列。在这个序列上有两个操作。
其中:
“C a b c”表示区间[a,b]里的所有数都增加c。
“Q a b”表示求出区间[a,b]里的所有数的和。
思路: 线段树+lazy思想:
http://www.java3z.com/cwbwebhome/article/article1/1365.html?id=4772
线段树也叫区间树,顾名思义,线段树是一种基于区间的树,每个节点表示一个"线段"或"区间"。树的根节点表示是"整体"的区间,左右子树分别表示这个区间的左半边和右半边。
假设要构造一个表示N个区间大小的线段树,线段树的根节点表示区间[0,N-1],然后将区间分成两半,分别为左右子树表示,这样的线段树的节点只有2N-1个,是O(N)级别的,如图所示表示构造区间[1-5]的线段树。
线段树可以使用链状结构动态构造,这样的优点是节省空间,空间复杂度在2N,但是缺点就是编程复杂度大,执行效率低;
所以线段树一般是使用一维数组以完全二叉树的方式来存储,类似堆,这样的编程复杂度低,执行效率高,但是耗费空间,一般来讲,这样情况下都是申请4N的空间.
例: HDU1754
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
Output
对于每一次询问操作,在一行里面输出最高成绩。
Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
Sample Output
5
6
5
9
- class Tree { //线段树节点,
- public Tree L, R;
- public int ValueL, ValueR;
- public int maxscore;
- }
- static int score[] = new int[200010];
- static class IntervalTree { //线段树,链式实现
- public int pos;
- public Tree nodes[] = new Tree[400010];
- public IntervalTree()
- {
- for (int j = 0; j < 400010; j++) {
- nodes[j] = new Tree(); //所有节点先构建
- }
- }
- public Tree Build(int L, int R) //建树
- {
- Tree rootTree = nodes[pos++];
- rootTree.ValueL = L;
- rootTree.ValueR = R;
- if (L == R) {
- rootTree.maxscore = Math.max(score[L], score[R]);
- return rootTree;
- } else {
- int mid;
- mid = (L+R)/2;
- rootTree.L = Build(L, mid);
- rootTree.R = Build(mid+1, R);
- rootTree.maxscore = Math.max(rootTree.L.maxscore, rootTree.R.maxscore);
- }
- return rootTree;
- }
- public int Query(Tree root, int LL, int RR) //查询
- {
- int ret = 0;
- if (LL == root.ValueL && RR == root.ValueR) {
- return root.maxscore;
- } else {
- int mid;
- mid = (root.ValueL+root.ValueR)/2;
- if (RR <= mid)
- ret = Query(root.L, LL, RR);
- else if (LL > mid)
- ret = Query(root.R, LL, RR);
- else
- ret = Math.max(Query(root.L, LL, mid), Query(root.R, mid + 1, RR));
- return ret;
- }
- }
- public int Update(Tree root, int RR, int value) { //更新
- int ret = 0;
- int mid = (root.ValueL + root.ValueR)/2;
- if (RR == root.ValueL && RR == root.ValueR) {
- return root.maxscore = value;
- }
- if (RR <= mid)
- ret = Update(root.L, RR, value);
- else
- ret = Update(root.R, RR, value);
- root.maxscore= Math.max(root.maxscore,ret);
- return root.maxscore;
- }
- //中序遍历二叉线段树
- private void printTree(Tree root) {
- if( root!= null ){
- printTree( root.L);
- System.out.print("["+root.ValueL+","+root.ValueR+"] ");
- printTree( root.R );
- }
- }
- }
在那里用链状结构实现了二叉线段树,下面程序使用一维数组以完全二叉树的方式来存储。
POJ3468
大致题意:给出一个长度为n的整数序列。在这个序列上有两个操作。
其中:
“C a b c”表示区间[a,b]里的所有数都增加c。
“Q a b”表示求出区间[a,b]里的所有数的和。
思路: 线段树+lazy思想:
- import java.io.BufferedInputStream;
- class Tree{ //线段树节点
- int l, r;
- long v, add;
- }
- public class Main{//数组实现的二叉线段树
- static int N= 100005;
- Tree node[]=new Tree[N*3];
- long sum[];
- public Main(long[] sum){
- this.sum=sum;
- for(int i=0;i<3*N;i++)
- node[i]=new Tree();
- }
- public int L(int u){
- return u << 1;
- }
- public int R(int u) {
- return u << 1 | 1 ;
- }
- public void build ( int u, int l, int r ) //建以r为根的线段树[l,r]
- {
- node[u].l = l;
- node[u].r = r;
- node[u].add = 0;
- node[u].v = sum[r] - sum[l-1];
- if ( l == r ) return;
- int mid = ( l + r ) >> 1;
- build ( L(u), l, mid );
- build ( R(u), mid+1, r );
- }
- public void update ( int u, int l, int r, int val ) //更新
- {
- if ( l == node[u].l && node[u].r == r )
- {
- node[u].add += val;
- node[u].v += val * ( r - l + 1 );
- return;
- }
- if ( node[u].add != 0 )
- {
- node[L(u)].add += node[u].add;
- node[L(u)].v += node[u].add * ( node[L(u)].r - node[L(u)].l + 1 );
- node[R(u)].add += node[u].add;
- node[R(u)].v += node[u].add * ( node[R(u)].r - node[R(u)].l + 1 );
- node[u].add = 0;
- }
- int mid = ( node[u].l + node[u].r ) >> 1;
- if ( r <= mid )
- update ( L(u), l, r, val );
- else if ( l > mid )
- update ( R(u), l, r, val );
- else
- {
- update ( L(u), l, mid, val );
- update ( R(u), mid+1, r, val );
- }
- node[u].v = node[L(u)].v + node[R(u)].v;
- }
- public long query ( int u, int l, int r ) //查询
- {
- if ( l == node[u].l && node[u].r == r )
- return node[u].v;
- if ( node[u].add != 0 )
- {
- node[L(u)].add += node[u].add;
- node[L(u)].v += node[u].add * ( node[L(u)].r - node[L(u)].l + 1 );
- node[R(u)].add += node[u].add;
- node[R(u)].v += node[u].add * ( node[R(u)].r - node[R(u)].l + 1 );
- node[u].add = 0;
- }
- int mid = ( node[u].l + node[u].r ) >> 1; // 计算左右子结点的分隔点
- if ( r <= mid)
- return query ( L(u), l, r ); // 和左孩子有交集,考察左子结点
- else if ( l > mid )
- return query ( R(u), l, r ); // 和右孩子有交集,考察右子结点
- else
- return query ( L(u), l, mid ) + query ( R(u), mid+1, r );
- }
- public static void main(String args[]){
- Scanner in = new Scanner(new BufferedInputStream(System.in));
- int n = in.nextInt();
- int m = in.nextInt();
- long[] sum=new long[n+1];
- for(int i = 1; i<=n; i++){
- sum[i] = sum[i-1] + in.nextInt();
- }
- Main st=new Main(sum);
- st.build(1,1,n);
- //st.printTree(1);
- String cmd;
- int x;
- int y;
- int c;
- for(int i = 0; i<m; i++)
- {
- cmd = in.next();
- if (cmd.equals("Q")) {
- x = in.nextInt();
- y = in.nextInt();
- System.out.println(st.query(1,x, y));
- } else {
- x = in.nextInt();
- y = in.nextInt();
- c=in.nextInt();
- // System.out.println("c="+c);
- st.update(1,x,y,c);
- }
- }
- }
- }
http://www.java3z.com/cwbwebhome/article/article1/1365.html?id=4772
class Tree{ //线段树节点类 int left; int right; long sum; long add; } public class Main{ static int M= 300009;//节点个数 int a[]; Tree[] tree=new Tree[M];//所有节点 public Main(int[] a){ this.a=a; for(int i=0;i< M;i++) tree[i]=new Tree(); } public int Mid(int a,int b){//求中点 return (a+b)>>1; } /* * 建树 * */ void build(int id,int left,int right){ //以id为根建线段树 tree[id].left=left; tree[id].right=right; if(left==right){ tree[id].sum=a[left]; tree[id].add=0; return; } int mid=Mid(left,right); build(id*2,left,mid); //建左树 build(id*2+1,mid+1,right); //建右树 tree[id].sum=tree[id*2].sum+tree[id*2+1].sum; } //中序遍历以i为根二叉线段树 private void printTree(int i) { if( tree[i].left!=0 ){ printTree(2*i); System.out.print("["+tree[i].left+","+tree[i].right+"] "); printTree(2*i+1); } } //懒操作 void push_down(int id){ //需要的时候更新id节点保存的和sum,并把更新标记下传到子节点 if(tree[id].add!=0){ //需要更新 tree[id*2].add+=tree[id].add; tree[id*2].sum += tree[id].add * ( tree[2*id].right - tree[2*id].left + 1 ); tree[id*2+1].add+=tree[id].add; tree[id*2+1].sum+=tree[id].add * ( tree[2*id+1].right - tree[2*id+1].left + 1 ); tree[id].add=0; } } /* * 更新区间:把区间[left,right]同时增加key * */ void update_add(int id,int left,int right,int key){ if(tree[id].left==left&&tree[id].right==right){//找到操作区间了 tree[id].add+=key; //标记 tree[id].sum+=key*(right-left+1); return; } push_down(id); int mid=Mid(tree[id].left,tree[id].right); if(right<=mid){ update_add(id*2,left,right,key); }else if(left>mid){ update_add(id*2+1,left,right,key); }else{ update_add(id*2,left,mid,key); update_add(id*2+1,mid+1,right,key); } tree[id].sum=tree[id*2].sum+tree[2*id+1].sum;//更新父节点 } /* * 查询区间 * */ long query(int id,int left,int right){ if(tree[id].left==left&&tree[id].right==right){ return tree[id].sum; } push_down(id); //先更新此节点,并把更新标记传到子节点 int mid=Mid(tree[id].left,tree[id].right); if(right<=mid){ return query(id*2,left,right); }else if(left>mid){ return query(id*2+1,left,right); }else{ return query(id*2,left,mid)+query(id*2+1,mid+1,right); } } public static void main(String args[]){ Scanner in = new Scanner(new BufferedInputStream(System.in)); int n = in.nextInt(); int m = in.nextInt(); int[] a=new int[n+1]; for(int i = 1; i<=n; i++) a[i] = in.nextInt(); Main st=new Main(a); st.build(1,1,n); st.printTree(1); String cmd; int x; int y; int c; for(int i = 0; i < m; i++) { cmd = in.next(); if (cmd.equals("Q")) { x = in.nextInt(); y = in.nextInt(); System.out.println(st.query(1,x, y)); } else { x = in.nextInt(); y = in.nextInt(); c=in.nextInt(); // System.out.println("c="+c); st.update_add(1,x,y,c); } } } }Read full article from 线段树入门学习(兼解HDU1754) - 心中的利剑:HTML5 Canvas游戏动画(JavaScript) - ITeye技术网站