线段树
作用
1、单点修改
2、区间查询
相比于树状数组,线段树代码更复杂但应用更广泛
例题 Acwing 1264. 动态求连续区间和
给定 n n n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [ a , b ] [a,b] [a,b]的连续和。
输入格式
第一行包含两个整数 n n n和 m m m,分别表示数的个数和操作次数。
第二行包含 n n n 个整数,表示完整数列。
接下来 m m m 行,每行包含三个整数 k , a , b k,a,b k,a,b( k = 0 k=0 k=0,表示求子数列 [ a , b ] [a,b] [a,b]的和; k = 1 k=1 k=1,表示第 a a a 个数加 b b b )。
数列从 1 1 1 开始计数。
输出格式
输出若干行数字,表示 k = 0 k=0 k=0 时,对应的子数列 [ a , b ] [a,b] [a,b] 的连续和。
数据范围
1 ≤ n ≤ 100000 1≤n≤100000 1≤n≤100000,
1 ≤ m ≤ 100000 1≤m≤100000 1≤m≤100000,
1 ≤ a ≤ b ≤ n 1≤a≤b≤n 1≤a≤b≤n,
数据保证在任何时候,数列中所有元素之和均在 i n t int int 范围内。
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
Java代码
import java.util.*;
class Main
{public static class Tr{int l;int r;int sum;}public static Tr[] tr = new Tr[400000]; //四倍Npublic static int[] w = new int[100010];public static void main(String[] args){Scanner sc = new Scanner(System.in);int n=sc.nextInt();int m=sc.nextInt();for(int i=1;i<=n;i++){w[i]=sc.nextInt();}build(1,1,n); //建立线段树while(m-->0){int k=sc.nextInt();int a=sc.nextInt();int b=sc.nextInt();if(k==0) System.out.println(query(1,a,b));else modify(1,a,b);}}public static void pushup(int x){tr[x].sum = tr[x<<1].sum+tr[x<<1|1].sum;}public static void build(int x,int l,int r){tr[x]=new Tr();tr[x].l=l;tr[x].r=r;if(l==r){tr[x].sum=w[l];}else //这里else不能忘,否则报错 Index out of bounds for length{int mid=l+r>>1;build(x<<1,l,mid); //递归左子树build(x<<1|1,mid+1,r); //递归右子树pushup(x);}}public static int query(int x,int l,int r){if(tr[x].l>=l && tr[x].r<=r) return tr[x].sum;int mid=tr[x].l+tr[x].r>>1;int sum=0;if(l <= mid) sum+=query(x<<1,l,r); //遍历左子树if(r > mid) sum+=query(x<<1|1,l,r); //遍历右子树return sum;}public static void modify(int x,int u,int v){if(tr[x].l == tr[x].r) tr[x].sum+=v;else{int mid=tr[x].l+tr[x].r>>1;if(tr[x].l<=u && u<=mid) modify(x<<1,u,v);if(u>=mid+1 && u<=tr[x].r) modify(x<<1|1,u,v);pushup(x);}}
}