2018.10.22【COGS2632】数列操作d(线段树)

本文介绍了一种结合线段树与等差数列的算法,用于高效处理区间加法与求和问题。通过将一次函数应用于线段树节点,实现了区间更新与查询的快速操作。

传送门


解析:

在经历了奋斗一上午发现模数取错的尴尬之后,zxyoi终于AC了这道题。

思路:

简化题意就是线段树区间加等差数列,询问区间和。

换句话说就是区间加了一个一次函数。

我们转换一下就发现一次函数是可以轻松合并的,所以我们转化成一个只与位置有关的一次函数,使他满足加法合并的性质。

然后更新就是直接一次函数各项加法,等差数列求和公式更新区间和


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define int ll

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline void outint(ll a){
	static char ch[23];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=300005;
cs int mod=1000000007;

ll sum[N<<2];
ll head[N<<2],d[N<<2];

inline void pushnow(int k,cs int &sk,cs int &b,cs int &l,cs int &r){
	head[k]=(head[k]+b)%mod,d[k]=(d[k]+sk)%mod;
	sum[k]=(1ll*sum[k]+1ll*(r-l+1)*b+1ll*(r+l)*(r-l+1)/2*sk%mod)%mod;
}

inline void pushdown(int k,int l,int r){
	if(head[k]||d[k]){
		int mid=(l+r)>>1;
		pushnow(k<<1,d[k],head[k],l,mid);
		pushnow(k<<1|1,d[k],head[k],mid+1,r);
		head[k]=d[k]=0;
	}
}

inline void pushup(int k){
	sum[k]=(1ll*sum[k<<1]+sum[k<<1|1])%mod;
}

void modify(int k,int l,int r,cs int &ql,cs int &qr,cs int &sk,cs int &b){
	if(ql<=l&&r<=qr){
		pushnow(k,sk,b,l,r);
		return ;
	}
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)modify(k<<1,l,mid,ql,qr,sk,b);
	if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr,sk,b);
	pushup(k);
}

ll query(int k,int l,int r,cs int &ql,cs int &qr){
	if(ql<=l&&r<=qr)return sum[k];
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(qr<=mid)return query(k<<1,l,mid,ql,qr);
	if(ql>mid)return query(k<<1|1,mid+1,r,ql,qr);
	return (1ll*query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr))%mod;
}

int n,m;
signed main(){
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	n=getint();
	m=getint();
	while(m--){
		int op=getint(),l=getint(),r=getint();
		switch(op){
			case 1:{
				int x=getint();
				modify(1,1,n,l,r,x,mod-l*x%mod);
				break;
			}
			case 0:{
				outint(query(1,1,n,l,r));pc('\n');
				break;
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值