package 洛谷;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class P3372模板线段树1
{
static final int MAX_SIZE=1000001;
static int tree[]=new int[MAX_SIZE<<2];//n*4必定不过界
static int lazyTag[]=new int[MAX_SIZE<<2];//懒惰标记
static int a[]=new int[MAX_SIZE];//需要构建线段树的数组
static int N,M;//N当前数据量 M操作数个数
public static void main(String[] args) throws IOException
{
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
String[]strings=bufferedReader.readLine().trim().split(" ");
N=Integer.parseInt(strings[0]);M=Integer.parseInt(strings[1]);
// tree=new int[N<<2];lazyTag=new int[N<<2];a=new int[N];
strings=bufferedReader.readLine().trim().split(" ");
for (int i = 0; i < strings.length; i++)
{
a[i+1]=Integer.parseInt(strings[i]);
}
build(1, 1, N);
for (int i = 0; i < M; i++)
{
strings=bufferedReader.readLine().trim().split(" ");
int opr=Integer.parseInt(strings[0]);
if (opr==1)
{
int x=Integer.parseInt(strings[1]);
int y=Integer.parseInt(strings[2]);
int k=Integer.parseInt(strings[3]);
update(x, y, 1, 1, N, k);
}
else
{
int x=Integer.parseInt(strings[1]);
int y=Integer.parseInt(strings[2]);
System.out.println(query(x, y, 1, N, 1));
}
}
}
/**
* 左孩子的索引
* @param p 当前节点
* @return
*/
public static int leftChild(int p)
{
return p<<1;//p*2
}
/**
* 右孩子的索引
* @param p 当前节点
* @return
*/
public static int rightChild(int p)
{
return (p<<1)|1;//p*2+1
}
/**
* 先去整合子节点的信息,再向它们的祖先回溯整合之后的信息
* 向上传导信息
* @param p 当前节点
*/
public static void pushUp(int p)
{
tree[p]=tree[leftChild(p)]+tree[rightChild(p)];
}
/**
* 建树
* @param p 当前节点
* @param l 左边界
* @param r 右边界
*/
public static void build(int p,int l,int r)
{
lazyTag[p]=0;
//如果左右区间相同,那么必然是叶子节点啦,只有叶子节点是被真实赋值的
if (l==r)
{
tree[p]=a[l];
return;
}
int mid=(l+r)>>1;// /2
build(leftChild(p), l, mid);
build(rightChild(p), mid+1, r);
pushUp(p);
}
/**
* 区间更新
* 单点修改就是区间更新的特例
* @param needL 需要修改的左边界
* @param needR 需要修改的右边界
* @param p 当前节点
* @param l 当前节点所存储的区间的左边界
* @param r 当前节点所存储的区间的右边界
* @param k 修改的值
*/
public static void update(int needL,int needR,int p,int l,int r,int k)
{
if (needL<=l&&r<=needR)
{
tree[p]+=k*(r-l+1);
lazyTag[p]+=k;
return;
}
pushDown(p,l,r);
int mid=(l+r)>>1;// /2
if (needL<=mid)
{
update(needL, needR, leftChild(p), l, mid, k);
}
if (needR>mid)
{
update(needL, needR, rightChild(p), mid+1, r, k);
}
//更新完子节点后更新自己
pushUp(p);
}
/**
* 向下更新
* @param p 当前节点
* @param l 左边界
* @param r 右边界
*/
public static void pushDown(int p, int l, int r)
{
int mid=(l+r)>>1;// /2
//每次更新两个儿子节点
f(leftChild(p),l,mid,lazyTag[p]);
f(rightChild(p),mid+1,r,lazyTag[p]);
lazyTag[p]=0;//本节点不再lazy了
}
/**
* 记录当前节点所代表的区间
* @param p 当前节点
* @param l 左边界
* @param r 右边界
* @param k 要更新的值
*/
public static void f(int p, int l, int r, int k)
{
lazyTag[p]=lazyTag[p]+k;
tree[p]=tree[p]+k*(r-l+1);//由于是这个区间统一改变故改变多少,就增加多少
}
/**
* 区间查询
* @param queryX 查询左边界
* @param queryY 查询右边界
* @param l 左边界
* @param r 右边界
* @param p 当前节点
*/
public static int query(int queryX, int queryY, int l, int r, int p)
{
int result=0;
if (queryX<=l&&r<=queryY)
{
return tree[p];
}
int mid=(l+r)>>1;// /2
pushDown(p, l, r);
if (queryX<=mid)
{
result+=query(queryX, queryY, l, mid, leftChild(p));
}
if (queryY>mid)
{
result+=query(queryX, queryY, mid+1, r, rightChild(p));
}
return result;
}
}