线段树练习1

题目大意:

桌子上零散地放着若干个盒子,桌子的后方是一堵墙。如右图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?
数据范围:
l表示墙的长度 1<=l<=100000
n表示盒子数量 1<=n<=100000
数据保证盒子的坐标

解题思路:

线段树
用cover记录该区间的状态,1表示被完全覆盖,0表示没有被完全覆盖。

源程序:

#include
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;
}

你可能感兴趣的:(线段树)