#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int val;//和
int fir;//首项
int pub;//公差
};
node t[100000];
int base[100000];
void push_up(int root)
{
t[root].val=t[root<<1].val+t[root<<1|1].val;
return ;//处理和
}
void push_down(int root,int l,int m,int r)
{
t[root<<1].fir+=t[root].fir;//处理首项,将等差数列拆分
t[root<<1|1].fir+=(t[root].fir+t[root].pub*(m-l+1));
t[root<<1].pub+=t[root].pub;//处理公差
t[root<<1|1].pub+=t[root].pub;
t[root<<1].val+=t[root].fir*(m-l+1)+t[root].pub*(m-l+1)*(m-l)/2;//使用等差数列的公式计算左右区间的和
t[root<<1|1].val+=t[root].fir*(r-m)+t[root].pub*(m-l+1+r-l)*(r-m)/2;//如果看的乱的话,可以不看我的,自己手推一波
t[root].fir=0;//置空
t[root].pub=0;
return ;
}//下放标记
void build(int root,int l,int r)
{
if(l==r)
{
t[root].val=base[l];
return ;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
return ;
}//很简单的建树
void change(int root,int l,int r,int al,int ar,int num,int k)
{
if(l>=al&&r<=ar)
{
t[root].val+=(num*(r-l+1)+k*(r-l+1)*(r-l)/2);
t[root].fir+=num;
t[root].pub+=k;
return ;
}
int mid=(l+r)>>1;
push_down(root,l,mid,r);
int chg=num+k*(mid-al+1);
if(mid>=al&&mid+1<=ar) //最恶心的改变,需要判断分裂的左右区间是只有一个区间包括所要修改区间,还是都包含
{//都包含
change(root<<1,l,mid,al,ar,num,k);
change(root<<1|1,mid+1,r,al,ar,chg,k);//将所要添加的等差数列分裂,chg为计算出来的右区间的首项
push_up(root);
return ;
}
if(mid>=ar)//所修改的区间只在左区间中
change(root<<1,l,mid,al,ar,num,k);
if(mid+1<=al)//只在右区间中
change(root<<1|1,mid+1,r,al,ar,num,k);
push_up(root);
return ;
}
int check(int root,int l,int r,int al,int ar)
{
if(l>ar||r<al)
return 0;
if(l>=al&&r<=ar)
return t[root].val;
int mid=(l+r)>>1;
push_down(root,l,mid,r);
return check(root<<1,l,mid,al,ar)+
check(root<<1|1,mid+1,r,al,ar);//很简单的查询
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&base[i]);
build(1,1,n);
int a,b,c,d,e;
for(int i=1;i<=m;i++)
{
scanf("%d",&a);
if(a==1)
{
scanf("%d%d%d%d",&b,&c,&d,&e);
change(1,1,n,b,c,d,e);
}
else
{
scanf("%d%d",&b,&c);
printf("%d\n",check(1,1,n,b,c));
}
}
return 0;
}