线段树(英语:Segment tree)是一种二叉树形数据结构,1977年由Jon Louis Bentley发明[1],用以储存区间或线段,并且允许快速查询结构内包含某一点的所有区间。
一个包含{\displaystyle n}个区间的线段树,空间复杂度为{\displaystyle O(n\log n)},查询的时间复杂度则为{\displaystyle O(\log n+k)},其中{\displaystyle k}是符合条件的区间数量。
此数据结构亦可推广到高维度。
--维基百科
简单来说,就是对于一个给定的数组,以它的所有元素作为叶子节点构建一棵二叉树,其父结点储存子节点的和(也可以储存其他东西,如最大最小值)。其目的就是为了能够快速查询某个区间的和(或者其他),并且在改变某个(某些)数组元素以后仍能快速查询。
构建过程是从根到叶子的。假定数组有六个元素[1, 3, 5, 7, 9, 11],那我们构建出来的线段数组应如下图。
此外,每个点还应该保留它的区间信息,代表是哪个区间的和。叶子节点的区间则是[n,n]。
该网页讲的已经比较清楚了,不再复述。https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/
下面题目
【问题描述】一个有L个格子的染色板,每个格子编号为1,2,3……L,每个格子初始都染成了1号色。一共有T种不同的颜色,编号分别为1,2,3……T。可进行O次操作。对染色板的操作有2种。
(1)C(a,b,c) 意思是将染色板中编号为a~b的所有格子染成c号色(1≤a≤b≤L,1≤c≤T)
(2)P(a,b)意思是查询染色板中编号为a~b的格子染成的颜色种数。(1≤a≤b≤L)
【输入】 第一行有三个正整数,分别为L,T,O.
接下来有O行,每行为C a b c或P a b。(C、P是字母,a,b,c均为正整数) 【输出】输出有若干行,分别回答输入中每个查询的结果。
【样例输入】
6 4 5
C 1 2 2
C 2 3 3
P 1 3
C 3 6 4
P 1 6
【样例输出】
2
3
【数据规模】
1≤L≤100,000 1≤T≤30 1≤O≤100,000
注意到L和O的给定范围很大(1 <= L, O <= 100000)而T的给定范围很小(<30)。用普通的方法做会超时,故用线段树组。
这里先贴代码,语言Java
import java.util.Scanner;
import javax.net.ssl.SSLContext;
import javax.print.attribute.standard.RequestingUserName;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.omg.CORBA.ORBPackage.InconsistentTypeCode;
import javafx.stage.PopupWindow;
public class sgmTree {
private int st[];
private int num;
private int temp;
public sgmTree(int arr[], int n) {
int x = (int)(Math.ceil(Math.log(n)/Math.log(2)));
int max_size = (int)(2 * Math.pow(2, X) -1);
st = new int[max_size];
num = n;
constructSTUtil(arr, 0, n-1, 0);
}
private int getMid(int ss, int se) {return ss + (se-ss)/2;}
private int getSumUtil(int ss, int se, int qs, int qe, int si) {
if(qs<=ss && se <= qe)
return st[si];
if(qs > se || qe < ss)
return 0;
int mid = getMid(ss, se);
return (getSumUtil(ss, mid, qs, qe, 2*si+1) |
getSumUtil(mid+1, se, qs, qe, 2*si+2));
}
private void updateValueUtil(int ss, int se, int i, int diff, int si) {
if(ise)return;
if(ss != se) {
int mid = getMid(ss, se);
updateValueUtil(ss, mid, i, diff, 2*si+1);
updateValueUtil(mid+1, se, i, diff, 2*si+2);
}
if(ss == se) {
temp = st[si];
st[si] = diff;
}
else {
if(((st[2*si+1] & temp) != 0) || (((st[2*si+2] & temp) != 0)))
st[si] = diff | st[si];
else {
st[si] = diff | st[si];
st[si] = st[si] & (~temp);
}
}
return;
}
public void updateValue(int i, int value ) {
if(i < 0 || i > num)return;
updateValueUtil(0, num-1, i-1, (int)(Math.pow(2,value)), 0);
return;
}
public int getSum(int qs, int qe) {
if(qs < 1 || qe > num+1)return -1;
int sum = getSumUtil(0, num-1, qs-1, qe-1, 0);
int result = 0;
while(sum != 0) {
result++;
sum -= sum & (-sum);
}
return result;
}
public int constructSTUtil(int array[], int ss, int se, int si) {
if(ss == se) {
st[si] = (int)Math.pow(2, array[ss]);
return st[si];
}
else {
int mid = getMid(ss, se);
st[si] = (constructSTUtil(array, ss, mid, 2*si+1) |
constructSTUtil(array, mid + 1, se, 2*si+2));
return st[si];
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int L = input.nextInt();
int T = input.nextInt();
int O = input.nextInt();
int[] arr = new int[L];
for(int i = 0; i