线段树
节点记录区间左右坐标和区间颜色
如果父节点i表示[left,
right],那么父节点的左节点i*2表示区间[left, mid],有节点表示[mid+1, right],其中mid为left和right的中点
所有的节点存在tree数组里面
叶节点表示一个点,即区间左右坐标是一样的
根节点代表区间[1,
l]
三个主要操作:
建树:初始化函数init(),
根据题目情况修改所代表区间
插入/修改颜色值:
insert函数, 参数分别是所需要修改的区间的左右坐标,还有修改的颜色值 默认从根节点开始修改
查询函数:
query函数, 参数分别是所查询的区间的左右坐标,返回该区间含有的不同颜色的数量 默认从根节点开始找
有些题目数据量过大需要离散化,如果不离散化的话在建树与插入的时候多了太多的没必要的搜索和没必要的内存空间。
假设给定的区间是[1,3],[6,1000],我们可以如下离散
离散前坐标:1
3 6 1000
离散后坐标:1
2 3 4
这样就可以缩小范围,减少内存使用
函数discrete用于离散化
输入的区间全部存在discreteTree,然后放到discreteValue数组去排序,离散后的区间对应表放在resultValue数组
原来的区间与离散后的区间对应关系如右:
[discreteTree[i].left, discreteTree[i].right] ==> [resultValue[discreteTree[i].left],
resultValue[discreteTree[i].right]]
注意使用前初始化:
memset(visit,
false, sizeof(visit));
l
= 20010;//线段树表示的最大区间[1, l]
init();//线段树初始化
使用离散化前初始化
memset(discreteVisit,
false, sizeof(discreteVisit));
discreteNum
= 0;//离散数量清零
int
maxX = discrete();//离散获取离散后的最大值(是为了查询的时候只在 [1, maxX]里面查询 提高效率)
*/
//pku2528 相当于给区间染色, 统计有多少种不同的颜色
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 10000001;//节点数量
const int DISCRETE_MAXN = 10010*2;//离散之后节点数量
const int COLOR_NUM = 20001;//所染颜色数量
const int DEFAULT_COLOR = 0;//默认颜色
struct node{//线段树节点
int
left, right;//区间左右坐标
int
color;
};
node tree[DISCRETE_MAXN*4];
bool visit[COLOR_NUM];
int l, n;
node discreteTree[DISCRETE_MAXN*4];//离散点
bool discreteVisit[MAXN];//标志离散点
int discreteValue[DISCRETE_MAXN*4];//离散值
int discreteNum;//离散量
int resultValue[MAXN];//最终离散值
//离散化
//返回离散化后最大的值
int discrete()
{
int
i = 1;
scanf("%d",
&n);
for(i
= 1 ; i <= n ; i++){
scanf("%d
%d", &discreteTree[i].left, &discreteTree[i].right);
if(!discreteVisit[discreteTree[i].left]){
discreteValue[discreteNum++]
= discreteTree[i].left;
discreteVisit[discreteTree[i].left]
= true;
}
if(
!discreteVisit[discreteTree[i].right] ){
discreteValue[discreteNum++]
= discreteTree[i].right;
discreteVisit[discreteTree[i].right]
= true;
}
}
sort(discreteValue,
discreteValue+discreteNum);
int
j = 1;
for(
i = 0 ; i < discreteNum ; i++ ){ //离散化过程
discreteVisit[discreteValue[i]]
= false;
resultValue[discreteValue[i]]
= j++;
}
return
j-1;
}
//建树
//参数: 区间左边坐标, 区间右边坐标, 节点下标
void build(const int &left, const int &right, const int &nodeNum = 1)
{
tree[nodeNum].left
= left;
tree[nodeNum].right
= right;
tree[nodeNum].color
= DEFAULT_COLOR;
if
(left == right)//终结条件, 区间剩下一个点的时候
return
;
int
mid = (left + right) >> 1;//求中点
build(left,
mid, 2*nodeNum);//建左子树
build(mid+1,
right, 2*nodeNum+1);//建右子树
}
//建树的初始化函数
void init()
{
build(1,
l);//默认总区间是[1, l]
tree[1].color
= 1;//先给总的区间上一种颜色
}
//往线段树插入数据/修改数据
//参数: 区间左边坐标, 区间右边坐标, 修改的区间颜色, 节点下标
void insert(const int &left, const int &right, const int &color, const int &nodeNum = 1)
{
if
(left == tree[nodeNum].left && right == tree[nodeNum].right){//如果区间刚刚好为要填充的区间, 修改区间的颜色值,终止
tree[nodeNum].color
= color;
return
;
}
if
(DEFAULT_COLOR != tree[nodeNum].color){//区间已经染过颜色
tree[nodeNum*2].color
= tree[nodeNum*2+1].color = tree[nodeNum].color;//左右区间记录原来区间的颜色
tree[nodeNum].color
= DEFAULT_COLOR;//区间设置为未染色颜色
}
int
mid = (tree[nodeNum].left + tree[nodeNum].right) >> 1;//获取区间的中点
if
(right <= mid){//修改区间全在当前的左半部分区间
insert(left,
right, color, nodeNum*2);//去左子树修改颜色值
}else
if (left > mid){//修改区间全在当前的右半部分区间
insert(left,
right, color, nodeNum*2+1);//去右子树修改颜色值
}else{
insert(left,
mid, color, nodeNum*2);
insert(mid+1,
right, color, nodeNum*2+1);
}
}
//查询函数
//参数: 查询区间左坐标,
有坐标, 节点下标
//返回: 查询区间里面不同颜色的数量
int query(const int &left, const int &right, const int &nodeNum = 1)
{
int
count = 0;
if
(tree[nodeNum].color != DEFAULT_COLOR)
{
if
(visit[tree[nodeNum].color] == false)
{
count++;
visit[tree[nodeNum].color]
= true;
}
return
count;
}
int
mid = (tree[nodeNum].left + tree[nodeNum].right) >> 1;
if
(right <= mid){
count
+= query(left, right, nodeNum*2);
}else
if (left > mid){
count
+= query(left, right, nodeNum*2 + 1);
}else
{
count
+= query(left, mid, nodeNum*2);
count
+= query(mid+1, right, nodeNum*2 + 1);
}
return
count;
}
int main()
{
int
t, color;
scanf("%d",
&t);
memset(discreteVisit,
false, sizeof(discreteVisit));
while
(t--)
{
l
= 20010;
init();//线段树初始化
color
= 2;
discreteNum
= 0;
memset(visit,
false, sizeof(visit));
int
maxX = discrete();
for
(int i = 1; i <= n; ++i){
insert(resultValue[discreteTree[i].left],
resultValue[discreteTree[i].right], color++);
}
printf("%d\n",
query(1, maxX));
}
return
0;
}--*