线段数组 -以poj2777为例

线段树英语: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]。

线段数组 -以poj2777为例_第1张图片


该网页讲的已经比较清楚了,不再复述。https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/


下面题目

POJ2777 : http://poj.org/problem?id=2777

 【问题描述】一个有L个格子的染色板,每个格子编号为1,2,3……L,每个格子初始都染成了1号色。一共有T种不同的颜色,编号分别为1,2,3……T。可进行O次操作。对染色板的操作有2种。

 (1C(a,b,c) 意思是将染色板中编号为a~b的所有格子染成c号色(1≤a≤b≤L,1≤c≤T)

 2P(a,b)意思是查询染色板中编号为a~b的格子染成的颜色种数。(1≤a≤b≤L)


【输入】 第一行有三个正整数,分别为LTO.

接下来有O行,每行为C a b cP a b。(CP是字母,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



你可能感兴趣的:(线段数组 -以poj2777为例)