牛客49 D.筱玛爱线段树 (线段树区间修改+思维)

本文介绍了一种使用两棵线段树解决复杂区间操作问题的方法,通过倒序更新和懒惰传播技巧,有效地处理了区间加操作及递归性质的操作。详细解析了代码实现,包括初始化、更新、查询等核心步骤。

题意:有点难懂; 初始序列a[n],值都为0,有两个操作,

          1.区间【l,r】内数++,

          2.执行操作编号在[l,r]内的所有操作各一次,保证r小于当前操作的编号。m次操作结束后,你要告诉马爷A数组变成什么样子了。   意思就是说【l,r】区间(区间内的数此时为操作的编号)内的数即以前的操作编号的,按这些操作编号再操作一次。

题解:建两颗线段树,一颗维护处究竟每个操作1的区间加了多少次1.   把M次操作倒着更新维护,因为操作2有点递归的思想,正着无法在短时间内知道每个区间加多少1,

          第二棵在维护所有区间执行操作1后的,查询即可。

细节:就是第一课树实际是单点修改+区间修改,但是骚操作了一下,只用写一个区间修改。

//题外话:权值线段树没有区间修改

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
int op[maxn],l[maxn],r[maxn];
ll sum[maxn*4],lazy[maxn*4];
void push(int p){
	sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}
void pushdown(int l,int r,int p){
	int mid=(l+r)/2;
	if(lazy[p]){
		lazy[p<<1]+=lazy[p]; lazy[p<<1]%=mod;
		lazy[p<<1|1]+=lazy[p]; lazy[p<<1|1]%=mod;
		sum[p<<1]+=lazy[p]*(mid-l+1); sum[p<<1]%=mod;
		sum[p<<1|1]+=lazy[p]*(r-mid); sum[p<<1|1]%=mod;
		lazy[p]=0;
	}
}
void build(int l,int r,int p){
	if(l==r){
		sum[p]=0; lazy[p]=0;
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	push(p);
}
void update(int l,int r,int L,int R,ll val,int p){
	if(L<=l&&r<=R){
		lazy[p]+=val; lazy[p]%=mod;
		sum[p]+=val*(r-l+1); sum[p]%=mod;
		return ;
	}
	pushdown(l,r,p);
	int mid=(l+r)/2;
	if(L<=mid) update(l,mid,L,R,val,p<<1);
	if(R>mid)  update(mid+1,r,L,R,val,p<<1|1);
	push(p);
}
ll query(int l,int r,int L,int R,int p){
	if(L<=l&&r<=R){
		return sum[p]%mod;
	}
	pushdown(l,r,p);
	int mid=(l+r)/2;
	ll ans=0;
	if(L<=mid) ans+=query(l,mid,L,R,p<<1);
	if(R>mid) ans+=query(mid+1,r,L,R,p<<1|1); 
	return ans%mod;
}
ll tp[maxn];
int main(){
	int n,m; cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>op[i]>>l[i]>>r[i];
	}
	build(1,m,1);
	for(int i=m;i>=1;i--){
		if(op[i]==1) update(1,m,i,i,1,1);
		else{
			ll Q=query(1,m,i,i,1);//从后往前确定操作1的次数
			update(1,m,l[i],r[i],Q+1,1);
		}
	}
	for(int i=1;i<=m;i++){//每个操作1究竟执行几次
		tp[i]=query(1,m,i,i,1);
	}
	memset(lazy,0,sizeof(lazy));
	memset(sum,0,sizeof(sum));
	build(1,n,1);
	for(int i=1;i<=m;i++){
		if(op[i]==1){
			update(1,n,l[i],r[i],tp[i],1);
		}
	}
	for(int i=1;i<=n;i++){
		cout<<query(1,n,i,i,1)<<" ";
	}
	cout<<endl;
	
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值