涉及区间维护,我们可以用线段树来做。
开两个线段树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;
}