CF915E Physical Education Lessons(动态开点线段树)

CF915E Physical Education Lessons
CodeForces 不允许您多次提交相同的一份代码。如您多次提交相同代码,则有可能会被返回此错误。如确实需要多次提交一份代码,请自行添加一些注释 ~

题目大意

给出一个数列,初始每个元素都为1,每次操作把区间 ( l , r ) (l,r) (l,r)的所有元素变为1或者0,求每次操作后 ( 1 , n ) (1,n) (1,n)区间所有元素的和.

题目解析

涉及区间修改、区间求和的问题,自然想到用线段树来做.

但是观察数据范围,发现最多有 n ( n &lt; 1 e 9 ) n(n&lt;1e9) n(n<1e9)个元素,显然不能建完整的线段树,所幸询问 q q q的数据范围还算正常,所以我们考虑动态开点,点1表示区间 ( 1 , n ) (1,n) (1,n)的所有元素的和,用 t r e e . l tree.l tree.l t r e e . r tree.r tree.r维护左右子树,而且只有在需要子树时才给他们开点.

这样每次我们询问,就会向下开点,时间复杂度为 O ( q &MediumSpace; l o g &MediumSpace; q ) O(q\:log \:q) O(qlogq),空间复杂度为 O ( 2 ∗ q &MediumSpace; l o g &MediumSpace; q ) O(2*q\:log\:q) O(2qlogq),满足题目条件.

程序实现

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define maxn 15000010//1.5e7=3e5*25*2,log 3e5=25
using namespace std;
struct Tree{
	int val,l,r;
}tree[maxn];
int n,q,tag[maxn],cnt;
void push_down(int p,int l,int r){
	if(tag[p]==-1)return ;//同hotel,设了一个状态来表示不用下传懒标记
	if(!tree[p].l ) tree[p].l =++cnt;
	if(!tree[p].r ) tree[p].r =++cnt;
	tree[tree[p].l ].val =tag[p]*(mid-l+1);
	tree[tree[p].r ].val =tag[p]*(r-mid);//区间一起修改
	tag[tree[p].l ]=tag[p];
	tag[tree[p].r ]=tag[p];
	tag[p]=-1;
	return ;
}
int update(int p,int l,int r,int rl,int rr,int val){
	if(p==0)p=++cnt;
	if(rl<=l&&r<=rr){
		tag[p]=val;
		tree[p].val =val*(r-l+1);
		return p;//修改节点值
	}
	push_down(p,l,r);//下放懒标记
	if(rl<=mid) tree[p].l =update(tree[p].l ,l,mid,rl,rr,val);
	if(mid<rr) tree[p].r =update(tree[p].r ,mid+1,r,rl,rr,val);
	tree[p].val =tree[tree[p].l ].val +tree[tree[p].r ].val ;//更新父节点信息
	return p;
}
int main(){
	memset(tag,-1,sizeof tag);//-1表示不用下传懒标记的状态
	scanf("%d%d",&n,&q);
	int root=update(0,1,n,1,n,1);
	for(int i=1,l,r,k;i<=q;i++){
		scanf("%d%d%d",&l,&r,&k);
		if(k==1){
			int x=update(1,1,n,l,r,0);//更新对应区间
			printf("%d\n",tree[1].val );//1号节点表示区间(1,n)
		}
		else {
			int x=update(1,1,n,l,r,1);
			printf("%d\n",tree[1].val );
		}
	}
	return 0;
}

题后总结

这道题卡空间卡的很死,多开了一个1.5e7的数组都过不了.

既然是线段树,就一定要想到懒标记这种必要的优化.

要记得动态开点线段树,不过就是改变了一种维护父子关系的方法而已.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值