【COGS 2632】数列操作d

【题目】

传送门

题目描述:

一个长度为 n n n 的序列,一开始序列数的权值都是 0 0 0,有 m m m 次操作
支持两种操作:
1 1 1 l l l r r r x x x,给区间 [ l l l , r r r ] 内位置为 p o s pos pos 的数加上 ( p o s − l ) ∗ x (pos-l)*x (posl)x
0 0 0 l l l r r r,查询区间 [ l l l , r r r ] 内的权值和
最终答案对 1 0 9 + 7 10^9+7 109+7 取模。

输入格式:

第一行两个数 n n n m m m,表示序列长度和操作次数
接下来 m m m 行,每行描述一个操作,有如下两种情况:
1 1 1 l l l r r r x x x,给区间 [ l l l , r r r ] 内位置为 p o s pos pos 的数加上 ( p o s − l ) ∗ x (pos-l)*x (posl)x
0 0 0 l l l r r r,查询区间 [ l l l , r r r ] 内的权值和

输出格式:

每一个 0 0 0 操作输出一个整数模 1 0 9 + 7 10^9+7 109+7

样例数据:

输入
5 5
0 2 3
1 4 5 1
1 1 5 5
0 1 4
0 2 3

输出
0
30
15

提示:

【数据范围】
对于 30 % 30\% 30% 的数据 n , m n,m n,m 2000 2000 2000
对于 100 % 100\% 100% 的数据, n , m n,m n,m 300000 300000 300000
保证读入的都是非负整数,所有的 x x x 10000 10000 10000

【分析】

相当于是区间加上一个等差数列吧

除了维护区间和之外,我们还需要维护每个区间的等差数列的首项和公差

由于是等差数列,用线段树很好合并,首项和公差就直接相加,的话套公式求和就可以了

然后其他的就是线段树基础操作了

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define Mod 1000000007
using namespace std;
int n,m,L,R,x;
int sum[N<<2],begin[N<<2],num[N<<2];
void add(int root,int l,int r,int val,int start)
{
	num[root]=(num[root]+val)%Mod;
	begin[root]=(begin[root]+start)%Mod;
	sum[root]=(sum[root]+(2ll*start+(r-l)*val)*(r-l+1)/2%Mod)%Mod;
}
void pushdown(int root,int l,int r,int mid)
{
	if(num[root])
	{
		add(root<<1,l,mid,num[root],begin[root]);
		add(root<<1|1,mid+1,r,num[root],(begin[root]+1ll*num[root]*(mid-l+1)%Mod)%Mod);
		num[root]=0,begin[root]=0;
	}
}
void modify(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	{
		add(root,l,r,k,1ll*(l-L)*k%Mod);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(x<=mid)  modify(root<<1,l,mid,x,y,k);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,k);
	sum[root]=(sum[root<<1]+sum[root<<1|1])%Mod;
}
int query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return sum[root];
	int ans=0,mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(x<=mid)  ans=(ans+query(root<<1,l,mid,x,y))%Mod;
	if(y>mid)  ans=(ans+query(root<<1|1,mid+1,r,x,y))%Mod;
	return ans;
}
int main()
{
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	int s,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&s,&L,&R);
		if(s==0)  printf("%d\n",query(1,1,n,L,R));
		if(s==1)  scanf("%d",&x),modify(1,1,n,L,R,x);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值