【线段树】COGS 2632

参考博客,作者:dreaming__ldx,来源:CSDN


数列操作传送门

【题目描述】

一个长度为n的序列,一开始序列数的权值都是0,有m次操作

支持两种操作:

1 L R x,给区间[L,R]内位置为pos的数加上(pos-L)*x

0 L R,查询区间[L,R]内的权值和

最终答案对109+7取模。

【输入】

第一行两个数n,m,表示序列长度和操作次数

接下来m行,每行描述一个操作,有如下两种情况:

1 L R x,给区间[L,R]内位置为pos的数加上(pos−L)×x

0 L R,查询区间[L,R]内的权值和

【输出】

 

每一个0操作输出一个整数模1e9+7

【样例输入】

5 5
0 2 3
1 4 5 1
1 1 5 5
0 1 4
0 2 3

【样例输出】

0
30
15 

【数据范围】

对于30%的数据 n,m<=2000

对于100%的数据,n,m<=300000

保证读入的都是非负整数,所有的x<=10000

 

【思路】 对于等差数列,注意到它是可以合并的。

比如两个等长的等差数列。一个等差数列S1,第一项为A1,公差为t1,另一个等差数列S2,第一项为A2,公差为t2。

那么把S1和S2加起来,就变成了一个第一项为(A1+A2),公差为(t1+t2)的新的等差数列。

现在只要我们知道等差数列的首项,公差和长度,就能求出数列的和。

那么对于每个区间[L,R],用两个标记维护一下这个区间的首项公差就行了。标记是可以合并的。

注意:这样做的前提是标记具有可合并性。反例:区间加等比数列

求和就是(首+末)*项数 /2。把数据带进去就是(首+(首+(项数-1)*公差))*(R-L+1) /2。

#include<bits/stdc++.h>
#define lc (root<<1)
#define rc (root<<1|1)
#define mid ((T[root].l+T[root].r)>>1)
#define ll long long
using namespace std;
const int maxn=3e5+5;
const ll mod=1e9+7;
int n,m,op,LL,RR,Val;
struct node{
	node(){l=r=sum=add=fir=0;}
	int l,r;
	ll sum,add,fir;
}T[maxn<<2];
inline void pushup(int root){T[root].sum=(T[lc].sum+T[rc].sum)%mod;}
inline void pushadd(int root,ll fir,ll v){
	(T[root].add+=v)%=mod,(T[root].fir+=fir)%=mod;
	(T[root].sum+=(T[root].r-T[root].l+1)*(2*fir+(T[root].r-T[root].l)*v)/2)%=mod;
}
inline void pushdown(int root){
	if(T[root].add){
		pushadd(lc,T[root].fir,T[root].add);
		pushadd(rc,T[root].fir+T[root].add*(mid-T[root].l+1),T[root].add);
		T[root].add=0,T[root].fir=0;
	}
}
void build(int root,int l,int r){
	T[root].l=l,T[root].r=r;
	if(l==r) return;
	build(lc,l,mid),build(rc,mid+1,r);
}
void update(int root,int L,int R,ll v){
	if(L<=T[root].l&&R>=T[root].r){
		pushadd(root,v*(T[root].l-LL),v);
		return;
	}
	pushdown(root);
	if(L<=mid) update(lc,L,R,v);
	if(R>mid) update(rc,L,R,v);
	pushup(root);
}
ll query(int root,int L,int R){
	if(L<=T[root].l&&R>=T[root].r) return T[root].sum;
	pushdown(root);
	ll ret=0;
	if(L<=mid) (ret+=query(lc,L,R))%=mod;
	if(R>mid) (ret+=query(rc,L,R))%=mod;
	return ret%mod;
}
inline void read(int &x){
	x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
int main(){
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	read(n),read(m);
	build(1,1,n);
	while(m--){
		read(op);
		if(op==1){
			read(LL),read(RR),read(Val);
			update(1,LL,RR,Val);
		}
		if(op==0){
			read(LL),read(RR);
			printf("%lld\n",query(1,LL,RR)%mod);
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值