维护一个数列
,支持两种操作:
1、1 L R K D
:给出一个长度等于
的等差数列,首项为
,公差为
,并将它对应加到
的每一个数上。即:令
。
2、2 P
:询问序列的第
个数的值
。
我们把
数组进行差分得到
,我们考虑每一次1操作
对
数组有什么影响。
我们可以发现,每一次的1操作
会让c[l]+=f,c[l+1..r]+=d,f[r+1]+=-f-(r-l)*d
(
表示首项,
表示公差)。
举个例子:
下标: 1 2...l l+1 l+2 l+3... r r+1 r+2...
a数组增量:+0 +0 +f +f+d +f+2*d +f+3*d +f+(r-l)*d +0 +0
c数组增量:+0 +0 +f +d +d +d +d -f-(r-l)*d +0
从这个表格中我们可以看到一些规律:我们发现直接差分需要将区间 的 都增加 ,所以我们可以联想到 。
讲讲线段树的实现。其实大家对线段树的基本概念都有了解(当默认),重点讲讲标记下传。为了节省时间,我们不能每次都递归到叶子节点计算,所以我们为每一个区间打上一个标记,代表这个区间所有数都加上了这个数。当我们需要遍历到一个节点,准备访问其叶子节点的时候,我们就把标记下放。因为标记下放的时间通常可以不计,默认为
,所以我们就在遍历节点的时候随便把标记下放了,没有增加时间复杂度又保证了正确性。
总的时间复杂度: ,可以通过本题。
const int N=1e5+100;
#define ll long long
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
char c=0;ll x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}//快读程序
ll sum[N<<2],add[N<<2];
inline void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
}//计算当前节点所代表的区间和
inline void pushdown(int o,int l,int r){
ll tag=add[o];add[o]=0ll;
add[o<<1]+=tag;add[o<<1|1]+=tag;
register int mid=(l+r)>>1;
sum[o<<1]+=tag*(mid-l+1);
sum[o<<1|1]+=tag*(r-mid);
}//标记下放,时间复杂度可认为是O(1)
void build(int o,int l,int r){
sum[o]=add[o]=0ll;
if (l==r) return;
register int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);return;
}//基于本题的建树操作
void updata(int o,int l,int r,int p,int q,ll v){
if (l>q||r<p) return;
if (p<=l&&r<=q){
sum[o]+=v*(r-l+1);
add[o]+=v;return;
}
if (add[o]) pushdown(o,l,r);
register int mid=(l+r)>>1;
updata(o<<1,l,mid,p,q,v);
updata(o<<1|1,mid+1,r,p,q,v);
pushup(o);return;
}//区间修改:把区间[p,q]的每一个数都加上v
ll query(int o,int l,int r,int p,int q){
if (l>q||r<p) return 0ll;
if (p<=l&&r<=q) return sum[o];
if (add[o]) pushdown(o,l,r);
register int mid=(l+r)>>1;
register ll ans=0ll;
ans+=query(o<<1,l,mid,p,q);
ans+=query(o<<1|1,mid+1,r,p,q);
return ans;
}//询问操作:询问区间[p,q]的总和
int n,m;ll a[N];
int main(){
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
ll opt=read(),l=read();
if (opt==1){
ll r=read(),f=read(),d=read();
if (l==r){
updata(1,1,n,l,l,f);
if (l!=n) updata(1,1,n,l+1,l+1,-f);
}
else{
updata(1,1,n,l,l,f);updata(1,1,n,l+1,r,d);
if (r!=n) updata(1,1,n,r+1,r+1,-f-(r-l)*d);
}
}
else printf("%lld\n",query(1,1,n,1,l)+a[l]);
}
return 0;
}