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

本文通过一道编程题《校门外的树(增强版)》详细介绍了如何使用两棵线段树来解决区间更新和查询的问题,并给出了完整的代码实现。

题目链接:P1276 校门外的树(增强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

好久没做线段树了,快要打济南区域赛了,来复习一下线段树*+*

题目是中文的,我在这就不说明题意了,注意,题目中问的问题:留下的树苗有多少颗,树苗是树被砍掉后种下的,所以在这我们要开两个线段树,一个用来存储原来的树的状态,另一个要存储树和树苗共存的形态,我们记录每次操作真实种植的树以及真实砍掉的树,什么叫真实种植的树呢?比如我们要对区间【l,r】种树,在这个区间种已经有了x棵树,那我们的真实种树量就应该是r-l+1-x棵,真实砍掉的树的计算方法同理。

先来看一下怎么求最终校门外留下的树苗的数目吧,最终校门外的树以及树苗数我们可以直接通过第二棵线段树一次查询直接求出,而最终校门外的树的数目我们可以直接通过第一棵线段树一次查询直接求出,这样两者作差就得到了这个问题的答案

现在我们来看一下怎么求出植树者种上又被砍掉的树苗有多少棵,过程中一共砍掉的树及树苗数我们已经在之前记录下来了,而我们在第一颗线段树种又可以通过一次查询直接求出当前剩余的树的数目,我们用一开始的树的数目减去最后剩余的树的数目就得到了在过程中砍掉的树的数目,这时候我们就可以用之前保存的砍掉的树及树苗的个数减去过程中砍掉的树的数目就得到了被砍掉的树苗的个数。

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=5e5+10;
int sum[2][N],l[2][N],r[2][N],lazy[2][N];
void pushup(int k,int id)
{
	sum[k][id]=sum[k][id<<1]+sum[k][id<<1|1];
} 
void pushdown(int k,int id)
{
	if(lazy[k][id]!=-1)
	{
		lazy[k][id<<1]=lazy[k][id];
		lazy[k][id<<1|1]=lazy[k][id];
		sum[k][id<<1]=(r[k][id<<1]-l[k][id<<1]+1)*lazy[k][id];
		sum[k][id<<1|1]=(r[k][id<<1|1]-l[k][id<<1|1]+1)*lazy[k][id];
		lazy[k][id]=-1;
	}
}
void build(int k,int id,int L,int R)
{
	l[k][id]=L;r[k][id]=R;sum[k][id]=0;lazy[k][id]=-1;
	if(L==R)
	{
		sum[k][id]=1;
		return ;
	}
	int mid=L+R>>1;
	build(k,id<<1,L,mid);
	build(k,id<<1|1,mid+1,R);
	pushup(k,id);
}
void update_interval(int k,int id,int L,int R,int val)
{
	if(l[k][id]>=L&&r[k][id]<=R)
	{
		sum[k][id]=(r[k][id]-l[k][id]+1)*val;
		lazy[k][id]=val;
		return ;
	}
	pushdown(k,id);
	int mid=l[k][id]+r[k][id]>>1;
	if(L<=mid) update_interval(k,id<<1,L,R,val);
	if(mid+1<=R) update_interval(k,id<<1|1,L,R,val);
	pushup(k,id);
}
int query_interval(int k,int id,int L,int R)
{
	if(L<=l[k][id]&&r[k][id]<=R) return sum[k][id];
	pushdown(k,id);
	int mid=l[k][id]+r[k][id]>>1;
	int ans=0;
	if(mid>=L) ans+=query_interval(k,id<<1,L,R);
	if(mid+1<=R) ans+=query_interval(k,id<<1|1,L,R);
	return ans;
}
int main()
{
	int l,n;
	cin>>l>>n;
	build(0,1,0,l);
	build(1,1,0,l);
	int ans1=0;//记录原始马路上的树被砍掉的数量
	int ans2=0;//记录实际植树的数量 
	for(int i=1;i<=n;i++)
	{
		int ll,rr,op;
		scanf("%d%d%d",&op,&ll,&rr);
		if(op==0)
		{
			ans1+=query_interval(0,1,ll,rr);//记录原始马路上的树中有多少棵被砍掉 
			update_interval(0,1,ll,rr,0);
			update_interval(1,1,ll,rr,0);
		}
		else
		{
			ans2+=rr-ll+1-query_interval(1,1,ll,rr);
			update_interval(1,1,ll,rr,1);
		}
	}
	printf("%d\n%d",query_interval(1,1,0,l)-query_interval(0,1,0,l),l+1+ans2-ans1-query_interval(1,1,0,l));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值