P1276 校门外的树(增强版) (线段树入门)

题目链接
在这里插入图片描述

涉及区间维护,我们可以用线段树来做。
开两个线段树tree[] 、 miao[] ,分别表示区间中树的个数,树苗的个数。(注意种下的全部是树苗)。

要求输出砍掉的树苗数,最终剩余的树苗数,
所以我们用ret记录砍掉的树苗数目,而剩余的树苗数目就是miao[root]

懒标记一共有4种:种树、砍树、先种后砍、先砍后种。
其中先种后砍与砍树的作用是等价的 ,
所以我们用1表示种树,2表示砍树,3表示先砍后种

区间更新以及标记的下放细节见代码。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e2+7;
int tree[maxn*4],miao[maxn<<2],tag[maxn*4];//tag 1表示种树 2表示砍树 3表示先砍后种 这里先种后砍等价于直接砍
int n,m;
int ret=0;//记录砍掉的树苗数量
void tree_build(int rt,int l,int r)
{
    if(l == r) tree[rt]=1;
    else 
    {
        int mid=(l+r)>>1;
        tree_build(rt<<1,l,mid);
        tree_build(rt<<1|1,mid+1,r);
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
}
inline void pushdown(int rt,int l,int r)
{
    int mid=(l+r)>>1;
    if(tag[rt] == 1)//种树
    {
        //种树只影响树苗数组
        miao[rt<<1]=(mid-l+1)-tree[rt<<1];
        miao[rt<<1|1]=(r-mid)-tree[rt<<1|1];
        //如果已经被砍过,传给子树的信息就是先砍后种
        if(tag[rt<<1]==2 || tag[rt<<1]==3)
            tag[rt<<1]=3;
        else tag[rt<<1]=1;
        //如果已经被砍过,传给子树的信息就是先砍后种
        if(tag[rt<<1|1]==2 || tag[rt<<1|1]==3)
            tag[rt<<1|1]=3;
        else tag[rt<<1|1]=1;
    }
    else if(tag[rt]==2)//砍树
    {
//砍树就全部归零,这里也可以看出先种后砍和直接砍没有区别,因为题目不要求我们记录种下的树苗数
        //注意这里不需要加入ret,因为在根节点区间砍的时候已经加过了
        miao[rt<<1]=miao[rt<<1|1]=0;
        tree[rt<<1]=tree[rt<<1|1]=0;
        tag[rt<<1]=tag[rt<<1|1]=2;
    }
    else if(tag[rt]==3)//先砍后种,和直接种的区别就是需要先把树数组清零,再维护苗数组
    {
        tree[rt<<1]=tree[rt<<1|1]=0;
        miao[rt<<1]=mid-l+1;
        miao[rt<<1|1]=r-mid;
        tag[rt<<1]=tag[rt<<1|1]=3;
    }
    tag[rt]=0;
}
inline void updata(int rt,int l,int r,int vl,int vr,int v)
{
    if(r<vl || l>vr) return;
    if(vl<=l && r<=vr)
    {
        if(v)//种树
        {
            miao[rt]=(r-l+1)-tree[rt];
            //如果已经被砍过,传给子树的信息就是先砍后种
            if(tag[rt]==2 || tag[rt]==3) tag[rt]=3;
            //否则就是种树
            else tag[rt]=1;
        }
        else//砍树 
        {
            ret+=miao[rt];
            tree[rt]=miao[rt]=0;
            tag[rt]=2;//不管原先的标记是什么 一但砍了就标记为砍树
        }
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt,l,r);
    updata(rt<<1,l,mid,vl,vr,v);
    updata(rt<<1|1,mid+1,r,vl,vr,v);
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    miao[rt]=miao[rt<<1]+miao[rt<<1|1];
}

int main()
{
    scanf("%d %d",&n,&m);
    tree_build(1,0,n);
    while(m--)
    {
        int f,x,y;
        scanf("%d %d %d",&f,&x,&y);
        updata(1,0,n,x,y,f);
    }
    printf("%d\n%d",miao[1],ret);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值