[b]线段树的构造思想 [/b]
线段树是一棵二叉树,树中的每一个结点表示了一个[b]区间[/b][a,b]。每一个叶子节点表示了一个单位区间。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2,b]。
例如:
[img]http://dl.iteye.com/upload/attachment/196564/edeb7986-abd1-3494-af91-410e6b915c58.jpg[/img]
[b]线段树的运用[/b]
线段树的每个节点上往往都增加了一些其他的域。在这些域中保存了某种动态维护的信息,视不同情况而定。这些域使得线段树具有极大的灵活性,可以适应不同的需求。
例1:求覆盖线段的总长度
[10000,22000] [30300,55000] [44000,60000] [55000,60000]
排序得10000,22000,30300,44000,55000,60000
对应得 1, 2, 3, 4, 5, 6
[1,2] [3,5] [4,6] [5,6]
线段树做法:
[img]http://dl.iteye.com/upload/attachment/196576/b34c84d9-7b8b-3f37-a66e-1d55100eaf9a.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/196578/8b0cf26c-08a4-3a4e-a779-1a872f4ffe10.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/196580/127e2c67-4cb9-30dd-999e-9126bfdc89ef.jpg[/img]
例2:[url]http://acm.pku.edu.cn/JudgeOnline/problem?id=2528[/url]
题意:贴海报,输出可以看到的个数.
[code="java"]
//贴海报,输出没有被覆盖的个数
//可以改成cpp试试
#include<stdio.h>
struct node
{
int l,r; //左右孩子的编号
int st,mi,en;
int id;
}; // 线段树简单一维
const int maxN = 50000002; //线段树的节点个数
const int maxL = 10000020; //台阶的宽度上限
node segment_tree[maxN]; //保存着线段树的所有节点
#define tree segment_tree
int root, ptr;
void insert(int cr, int start, int end, int color) //插入到指定区域,同时初始化沿途的所有节点
{
if(start >= end) //不符合输入要求
return;
if(tree[cr].st == start && tree[cr].en == end) //输入区间正好等于节点的表示区间
{
tree[cr].id = color; //这个区间属于该海报
return;
}
int mid = (tree[cr].st + tree[cr].en) / 2;
if(tree[cr].l == 0) //意味着还没有初始化孩子结点
{
//ptr代表节点的编号
tree[cr].l = ptr++;
tree[tree[cr].l].l = tree[tree[cr].l].r = 0;
tree[tree[cr].l].id = -1;
tree[tree[cr].l].st = tree[cr].st, //这里对左右孩子的范围进行初始化
tree[tree[cr].l].en = mid;
}
if(tree[cr].r == 0)
{
tree[cr].r = ptr++;
tree[tree[cr].r].l = tree[tree[cr].r].r = 0;
tree[tree[cr].r].id = -1;
tree[tree[cr].r].st = mid,
tree[tree[cr].r].en = tree[cr].en;
}
if(tree[cr].id != 0) //之后的子区间肯定都属于该海报
{
tree[tree[cr].l].id = tree[tree[cr].r].id = tree[cr].id;
tree[cr].id = 0;
}
if(start >= mid){
insert(tree[cr].r, start, end, color);
return;
}
if(end <= mid){
insert(tree[cr].l, start, end, color);
return;
}
insert(tree[cr].l, start, mid, color);
insert(tree[cr].r, mid, end, color);
}
char exist[10001];
void trail(int cr) //统计可以看见的节点编号
{
if(cr == 0 || tree[cr].id == -1)
return;
exist[tree[cr].id] = 1; //id不为0,意味着只有它可见,但之后的节点都看不见了.
if(tree[cr].id != 0) //不为0意味着后面的区域都要被覆盖.
return;
trail(tree[cr].l);
trail(tree[cr].r);
}
//初始化跟节点
void init()
{
root = 1;
tree.l = tree.r = tree.id = 0;
tree.st = 1, tree.en = maxL, tree.mi = (1 + maxL)/2;
ptr = 2;
}
int main()
{
int test,n,i,l,r;
scanf("%d", &test);
while(test--)
{
init();
scanf("%d",&n);
for(i = 1; i <= n; i++)
{
scanf("%d%d",&l,&r);
insert(1, l, r+1, i); //从根节点开始插入
}
for(i = 1; i <= n; i++)
exist[i] = 0;
trail(1);
int ans = 0;
for(i = 1; i <= n; i++)
if(exist[i])
ans++;
printf("%d\n",ans);
}
return 0;
}
[/code]
线段树是一棵二叉树,树中的每一个结点表示了一个[b]区间[/b][a,b]。每一个叶子节点表示了一个单位区间。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2,b]。
例如:
[img]http://dl.iteye.com/upload/attachment/196564/edeb7986-abd1-3494-af91-410e6b915c58.jpg[/img]
[b]线段树的运用[/b]
线段树的每个节点上往往都增加了一些其他的域。在这些域中保存了某种动态维护的信息,视不同情况而定。这些域使得线段树具有极大的灵活性,可以适应不同的需求。
例1:求覆盖线段的总长度
[10000,22000] [30300,55000] [44000,60000] [55000,60000]
排序得10000,22000,30300,44000,55000,60000
对应得 1, 2, 3, 4, 5, 6
[1,2] [3,5] [4,6] [5,6]
线段树做法:
[img]http://dl.iteye.com/upload/attachment/196576/b34c84d9-7b8b-3f37-a66e-1d55100eaf9a.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/196578/8b0cf26c-08a4-3a4e-a779-1a872f4ffe10.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/196580/127e2c67-4cb9-30dd-999e-9126bfdc89ef.jpg[/img]
例2:[url]http://acm.pku.edu.cn/JudgeOnline/problem?id=2528[/url]
题意:贴海报,输出可以看到的个数.
[code="java"]
//贴海报,输出没有被覆盖的个数
//可以改成cpp试试
#include<stdio.h>
struct node
{
int l,r; //左右孩子的编号
int st,mi,en;
int id;
}; // 线段树简单一维
const int maxN = 50000002; //线段树的节点个数
const int maxL = 10000020; //台阶的宽度上限
node segment_tree[maxN]; //保存着线段树的所有节点
#define tree segment_tree
int root, ptr;
void insert(int cr, int start, int end, int color) //插入到指定区域,同时初始化沿途的所有节点
{
if(start >= end) //不符合输入要求
return;
if(tree[cr].st == start && tree[cr].en == end) //输入区间正好等于节点的表示区间
{
tree[cr].id = color; //这个区间属于该海报
return;
}
int mid = (tree[cr].st + tree[cr].en) / 2;
if(tree[cr].l == 0) //意味着还没有初始化孩子结点
{
//ptr代表节点的编号
tree[cr].l = ptr++;
tree[tree[cr].l].l = tree[tree[cr].l].r = 0;
tree[tree[cr].l].id = -1;
tree[tree[cr].l].st = tree[cr].st, //这里对左右孩子的范围进行初始化
tree[tree[cr].l].en = mid;
}
if(tree[cr].r == 0)
{
tree[cr].r = ptr++;
tree[tree[cr].r].l = tree[tree[cr].r].r = 0;
tree[tree[cr].r].id = -1;
tree[tree[cr].r].st = mid,
tree[tree[cr].r].en = tree[cr].en;
}
if(tree[cr].id != 0) //之后的子区间肯定都属于该海报
{
tree[tree[cr].l].id = tree[tree[cr].r].id = tree[cr].id;
tree[cr].id = 0;
}
if(start >= mid){
insert(tree[cr].r, start, end, color);
return;
}
if(end <= mid){
insert(tree[cr].l, start, end, color);
return;
}
insert(tree[cr].l, start, mid, color);
insert(tree[cr].r, mid, end, color);
}
char exist[10001];
void trail(int cr) //统计可以看见的节点编号
{
if(cr == 0 || tree[cr].id == -1)
return;
exist[tree[cr].id] = 1; //id不为0,意味着只有它可见,但之后的节点都看不见了.
if(tree[cr].id != 0) //不为0意味着后面的区域都要被覆盖.
return;
trail(tree[cr].l);
trail(tree[cr].r);
}
//初始化跟节点
void init()
{
root = 1;
tree.l = tree.r = tree.id = 0;
tree.st = 1, tree.en = maxL, tree.mi = (1 + maxL)/2;
ptr = 2;
}
int main()
{
int test,n,i,l,r;
scanf("%d", &test);
while(test--)
{
init();
scanf("%d",&n);
for(i = 1; i <= n; i++)
{
scanf("%d%d",&l,&r);
insert(1, l, r+1, i); //从根节点开始插入
}
for(i = 1; i <= n; i++)
exist[i] = 0;
trail(1);
int ans = 0;
for(i = 1; i <= n; i++)
if(exist[i])
ans++;
printf("%d\n",ans);
}
return 0;
}
[/code]