P1047校门外的树(线段树初实践)

题目描述

某校大门外长度为 l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 的位置,另一端在 l的位置;数轴上的每个整数点,即 0,1,2,…,l,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式

第一行有两个整数,分别表示马路的长度 l 和区域的数目 m。

接下来 m 行,每行两个整数 u, v,表示一个区域的起始点和终止点的坐标。

输出格式

输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。

输入输出样例

输入           输出
500 3         298
150 300
100 200
470 471
说明/提示
数据规模与约定

对于 20% 的数据,保证区域之间没有重合的部分。
对于 100% 的数据,保证1≤l≤10e4
1≤m≤100

题目分析

对于本题可以开一个10000的数组,初始化为1,对于每一个对应区间的树就标记为0,最后将整个数组内的值求和即可。
在题解中看到了线段树的内容,作为一个刚学完数据结构的小白还不知道什么是线段树,借着这道题熟悉了一下线段树的基本内容和算法。
线段树类似于一个二叉排序树,只是排序的内容变成了集合的分割,即根节点的集合范围被分割成左孩子的集合和右孩子的集合,以此类推,直到叶子节点的集合左右边界相等。
下图就是一个典型的线段树实例
在这里插入图片描述

对于本题,则可以将每个目标区间内的树设置为0,利用线段树从根节点向下搜索,一旦节点的区间覆盖了该节点则将该节点的value设置为0,进行更新,最后求和即可。
lazytag:延迟标记,目前的理解是当不需要处理子孩子节点的更新数据时,直接标记lazytag,当需要用到该节点的子孩子数据时,将lazytag向下推,并清除该节点的lazytag.
对于此题,可知道修改区间有重叠。比如第一次[150-300]时讲[150-300]范围内的lazytag记为1,并将目的节点置为0.
然而当第二次区间为[100-200]时,出现了修改区间的重叠,这时候就需要用到上一次lazytag标记的节点的子节点,这个时候我们再进行pushdown操作将节点内容下推更新,提高了修改效率,不需要牵一发而动全身。
AC代码如下:

#include <stdio.h>
#include <stdlib.h>
#define MAXN 20000
//线段树

typedef struct segtree
{
    int left;//左端点
    int right;//右端点
    int value;//该区间内树的总和(权值)
    int lazytag;//懒惰标记 1或0
}tree;
tree segtree[MAXN*4];
void build(int left,int right,int u)//u为节点位置
{
    segtree[u].lazytag=0;//懒惰标记初始化为0
    segtree[u].left=left;
    segtree[u].right=right;
    if(left==right)
    {
        segtree[u].value=1;
        return;
    }
    int m=(left+right)/2;
    build(m+1,right,2*u+1);//右孩子
    build(left,m,2*u);//左孩子
    segtree[u].value=segtree[u+u].value+segtree[u*2+1].value;//父亲权值为孩子区间和
}
void pushdown(int u)
{
    segtree[u+u].lazytag=segtree[u].lazytag;//父亲节点的懒惰节点传给孩子
    segtree[u+u+1].lazytag=segtree[u].lazytag;//右孩子
    segtree[u+u].value=0;
    segtree[u+u+1].value=0;//该题则为0
    segtree[u].lazytag=0;//去除懒惰标记
}
void update(int u,int x,int y)//x为目的查询左区间,y为目的查询右区间
{
    if(segtree[u].left>=x&&segtree[u].right<=y)//覆盖该区间
{
    segtree[u].value=0;
    segtree[u].lazytag=1;//说明已经修改过
        return;
}
if(segtree[u].lazytag!=0)//已经覆盖了
{
    pushdown(u);//将下面的子区间值更新为0
}
int m=(segtree[u].left+segtree[u].right)/2;
if(x<=m)
{
    update(u+u,x,y);
}
if(y>m)
{
    update(u+u+1, x, y);
}
segtree[u].value=segtree[u+u].value+segtree[u+u+1].value;//更新值


}
int main()
{
    int m,n;
    scanf("%d %d",&n,&m);
    n++;
    int x,y;
    //n颗树m个区域
    build(1,n,1);
    while(m--)
    {
        scanf("%d %d",&x,&y);//一个区间
        x++;y++;
        //这里都将0-n变为1-(n-1)的集合定义域
        update(1,x,y);//更新这个区域的值
    }
    printf("%d",segtree[1].value);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值