1276 校门外的树(增强版)
我是来练习线段树的菜鸡
这个题在2月份就做过,可惜那个时候的我是个辣鸡,整天混日子…
好了话不多说了
提炼一下题目
给定一个长度为l的数列,在给定n个操作,1 l r代表将l ~ r之间的数全部清空,2 l r代表将l ~ r之间全部恢复
感觉还是挺水的
不过我感觉这个题评为橙色有点草率了
在这道题里面,体现出了懒标记的作用,比如我们要操作这个节点,标记这个节点砍掉种上或者砍掉又种上(当然在其他的题目中就可能是求和求积等),因为线段树存的是整个区间,所以也得需要对儿子进行下传更新,一步一步,从而对儿子进行更新,节省时间,达到必要性
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int SIZE=1e5+5;
int n,m;
int opt,x,y;
int wood[SIZE];//计算值
int miao[SIZE],out;
int flag[SIZE];//标记砍树
inline void push_down(int p,int l,int r)
{
if(!flag[p]) return ;
int mid=(l+r)>>1;
if(flag[p]==3)//砍掉又种上
{
wood[p*2]=wood[p*2+1]=0;
miao[p*2]=mid-l+1;
miao[p*2+1]=r-mid;
flag[p*2]=flag[p*2+1]=3;
flag[p]=0;
}
if(flag[p]==2)//砍掉
{
miao[p*2]=miao[p*2+1]=0;
wood[p*2]=wood[p*2+1]=0;
flag[p*2]=flag[p*2+1]=2;
flag[p]=0;
}
if(flag[p]==1)
{
miao[p*2]=(mid-l+1-wood[p*2]);
miao[p*2+1]=(r-mid-wood[p*2+1]);
//----
if(flag[p*2]==2||flag[p*2]==3) flag[p*2]=3;
else flag[p*2]=1;
if(flag[p*2+1]==2||flag[p*2+1]==3) flag[p*2+1]=3;
else flag[p*2+1]=1;
flag[p]=0;
}
}
inline void add(int from,int to,int l,int r,int p,int opt)//从l到r之间查找from到to的区间,节点为p,指令为opt
{
if(from<=l&&to>=r)//找到区间
{
if(opt)//种树
{
miao[p]=r-l+1-wood[p];
if(flag[p]==2||flag[p]==3) flag[p]=3;//砍掉又种上
else flag[p]=1;
}
else//砍树
{
out+=miao[p];
wood[p]=miao[p]=0;//清空
flag[p]=2;//被砍掉了
}
return ;
}
push_down(p,l,r);
int mid=(l+r)>>1;
if(from<=mid)
add(from,to,l,mid,p*2,opt);
if(to>mid)
add(from,to,mid+1,r,p*2+1,opt);
wood[p]=wood[p*2]+wood[p*2+1];
miao[p]=miao[p*2]+miao[p*2+1];
}
inline void build(int p,int l,int r)
{
if(l==r)//叶子节点
{
wood[p]=1;
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
wood[p]=wood[p*2]+wood[p*2+1];
}
int main()
{
cin>>n>>m;
n++;
build(1,1,n);
for(int i=1;i<=m;i++)
{
cin>>opt>>x>>y;
if(opt)
add(x+1,y+1,1,n,1,opt);
else
add(x+1,y+1,1,n,1,opt);
}
cout<<miao[1]<<endl;
cout<<out<<endl;
return 0;
}