传送门
解析:
在经历了奋斗一上午发现模数取错的尴尬之后,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;
}