欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客练习赛(49)筱玛爱线段树

程序员文章站 2022-07-12 17:39:19
...

筱玛爱线段树

题目描述

筱玛是一个热爱线段树的好筱玛。
筱玛的爷爷马爷在游戏中被筱玛吊打了,于是他恼羞成怒,决定给筱玛出这样一道数据结构题:
给定一个长度为n的数组AA,刚开始每一项的值均为0。
支持以下两种操作,操作共m次:
1 l r:将A_l~ A_r的每一项的值加上1。
2 l r:执行操作编号在[l,r]内的所有操作各一次,保证r小于当前操作的编号。
m次操作结束后,你要告诉马爷A数组变成什么样子了。
由于答案可能会很大,你只需要输出数组A中的每个数在模10^9+7意义下的值。

输入描述:

第一行两个数n,m,分别表示数组长度及操作次数。
接下来m行,每行三个数opt,l,r,表示一次操作。

输入

4 3
1 1 3
2 1 1
1 1 3

输出

3 3 3 0

题目意思已经很明显,但是容易忽略的是2操作包含的范围里面如果还有2操作那么又要进行叠加;
但是还是不好做啊,题目说是线段树,但是看了很多题解都是用差分来做;其实这道题也可以用线段树,不过是麻烦一点,建两颗树;一颗是以m为节点建一颗树,一颗是以n为节点建一颗树;先从后往前判断,当操作为2时,就进行add操作,这时要注意加的值是当前操作2时累计下来的值,不是1;进行完这个操作以后,就可以进行节点为n的树的操作,只要考虑操作为1就行,加上的值依然为操作1累加的值,不是1;最后逐个访问一下就行;

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL mod=1e9+7;
struct Node{
	int l,r;
	LL w,f;
}tree[400010];
int x,y,q;
LL z;
LL sum[100010];//记录每个操作可以做的次数 
LL sta;
int fx[100010],fy[100010],fq[100010];
inline void build(int k,int ll,int rr){
	tree[k].l=ll,tree[k].r=rr,tree[k].f=0;
	if(ll==rr){
		tree[k].w=sta;
		return;
	}
	int m=(ll+rr)>>1;
	build(k<<1,ll,m);
	build(k<<1|1,m+1,rr);
}
inline void pd(int k){
	if(tree[k].f){
		tree[k<<1].f=(tree[k<<1].f+tree[k].f)%mod;
		tree[k<<1|1].f=(tree[k].f+tree[k<<1|1].f)%mod;
		tree[k<<1].w=(tree[k<<1].w+tree[k].f*(tree[k<<1].r-tree[k<<1].l+1))%mod;
		tree[k<<1|1].w=(tree[k<<1|1].w+tree[k].f*(tree[k<<1|1].r-tree[k<<1|1].l+1))%mod;
		tree[k].f=0;
	}
}
inline void add(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		tree[k].w=(tree[k].w+(tree[k].r-tree[k].l+1)*z)%mod;
		tree[k].f=(tree[k].f+z)%mod;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) add(k<<1);
	if(y>m) add(k<<1|1);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%mod;
}
inline LL ask(int k){
	if(tree[k].l==tree[k].r){ 
		return tree[k].w;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) ask(k<<1);
	else ask(k<<1|1);
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	sta=1;//每个结点初始值, 
	build(1,1,m);//m个操作建树 
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&q,&x,&y);
		fx[i]=x,fy[i]=y,fq[i]=q;
	}
	for(int i=m;i>=1;i--){//反向询问,思考 
		if(fq[i]==2){
			x=i;
			z=ask(1);
			x=fx[i],y=fy[i];
			add(1);
		}
	}
	for(int i=1;i<=m;i++){
		x=i;
		sum[i]=ask(1);
	} 
	sta=0;//记得重置 
	build(1,1,n);//n个数组建树 
	for(int i=1;i<=m;i++){
		if(fq[i]==2) continue;
		z=sum[i];
		x=fx[i],y=fy[i];
		add(1);
	}
	for(int i=1;i<=n;i++){
		x=i;
		printf("%lld ",ask(1));
	}
	printf("\n");
	return 0;
}
相关标签: 线段树