题目大意:
桌子上零散地放着若干个盒子,桌子的后方是一堵墙。如右图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?
数据范围:
l表示墙的长度 1<=l<=100000
n表示盒子数量 1<=n<=100000
数据保证盒子的坐标
解题思路:
线段树
用cover记录该区间的状态,1表示被完全覆盖,0表示没有被完全覆盖。
源程序:
#include<cstdio>
using namespace std;
struct node{
int x,y;
bool cover;
}a[1000011];
int l,n,x1,y1;
void insert(int p,int b,int e)//插入算法
{
if (!a[p].cover)
//如果当前区间被完全覆盖,再插入盒子就已经没有任何意义
{
int mid=(a[p].x+a[p].y)/2;
if (a[p].x==b&&a[p].y==e)
a[p].cover=1;
//如果要插入盒子的区间就是当前区间,则这个区间被完全覆盖
//否则有三种情况
else if (e<=mid) insert(p*2,b,e);
//1.要插入盒子的区间在左孩子的区间范围内,则搜左孩子
else if (b>=mid) insert(p*2+1,b,e);
//2.要插入盒子的区间在右孩子的区间范围内,则搜右孩子
else {
insert(p*2,b,mid);
insert(p*2+1,mid,e);
//3.一部分在左孩子,一部分在右孩子,则两边都搜
//以中间值为分界线
}
}
}
int count_answer(int p)//统计答案
{
if (a[p].cover) return a[p].y-a[p].x;
else if (a[p].y-a[p].x==1) return 0;
else return count_answer(p*2)+count_answer(p*2+1);
//如果被完全覆盖,则返回整个盒子的长度
//如果它们表示的区间范围是1,又没有盒子,则退出(防止死循环然后爆掉)
//不然接着往下搜
}
void build(int i)//建树
{
if(a[i].y-a[i].x>1)
//如果它们表示的区间已经是1
//则说明不能继续分,就要退出循环,不然会爆掉
{
int mid=(a[i].x+a[i].y)/2;
a[i*2].x=a[i].x;
a[i*2+1].x=mid;
a[i*2].y=mid;
a[i*2+1].y=a[i].y;
build(i*2);
build(i*2+1);
}
}
int main()
{
scanf("%d",&l);
a[1].x=1;a[1].y=l;
build(1);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x1,&y1);
insert(1,x1,y1);
//每读入一个盒子,就要插入
}
printf("%d",count_answer(1));//统计,愉快地输出
return 0;
}