可能不是最标准的模板,但是正确,根据本人自己的理解写成,如果需要线段树的讲解,大概假期会做吧(挖坑)
#include <iostream>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
ll tree[4*MAXN], num[MAXN], n, m;
ll tag[4*MAXN];
void Build(int k, int l, int r) { //建树
if (l == r) { //如果当前节点是叶节点
tree[k] = num[l]; //将节点的值更新为数列中的值
return; //返回
}
int mid = (l + r) >> 1; //二分区间
Build(k << 1, l, mid); //建左子树
Build(k << 1 | 1, mid + 1, r); //建右子树
tree[k] = tree[k << 1] + tree[k << 1 | 1]; //当前节点的值更新为左右子区间的和
/*
if (k == 1) {
cout << "建立的线段树为:";
for (int i = 1; i <= 4 * n; i++) {
cout << tree[i] << " ";
}
cout << endl;
}
*/
}
void Down(int k, int l, int r, ll aw) { //下传标记
int mid = (l + r) >> 1; //二分区间
tag[k << 1] += aw ; //向左子节点传递
tree[k<<1]+=1ll*(mid-l+1)*aw;
tag[k << 1 | 1] += aw ; //向右子节点传递
tree[k<<1|1]+=1ll*(r-mid)*aw;
/*
cout << "标记下传,更新后标记为:";
for (int i = 1; i <= 4 * n; i++) {
cout << tag[i] << " ";
}
cout << endl;
*/
}
void Lazy_tag(int k, int l, int r, int x, int y, ll v) {//k->当前节点 l,r->当前节点表示区间 x,y->目标区间 v->修改值
if (l >= x && r <= y) { //如果当前区间完全包含需标记区间
tag[k] += v; //标记当前节点
tree[k]+=1ll*(r-l+1)*v; //更新当前节点的值
//printf("range %d %d:%lld\n",l,r,tree[k]);
/*
if (k == 1) {
cout << "线段树上的标记为:";
for (int i = 1; i <= 4 * n; i++) {
cout << tag[i] << " ";
}
cout << endl;
}
*/
return;
}
if (l > y || r < x) { //如果当前区间与目标区间完全不相关
return;
}
if (tag[k]) { //如果当前节点打有lazy_tag
if(l ^ r) { //如果不是叶节点
Down(k, l, r, tag[k]); //下传标记
}
tag[k] = 0; //清空当前标记
}
int mid = (l + r) >> 1; //二分两棵子树
if(x<=mid)Lazy_tag(k << 1, l, mid, x, y, v); //左子树
if(y>mid)Lazy_tag(k << 1 | 1, mid + 1, r, x, y, v); //右子树
tree[k]=tree[k<<1]+tree[k<<1|1];//更新当前节点的值
/*
if (k == 1) {
cout << "线段树上的标记为:";
for (int i = 1; i <= 4 * n; i++) {
cout << tag[i] << " ";
}
cout << endl;
}
*/
}
ll Find(int k, int l, int r, int x, int y) {//k->当前节点 l,r->当前节点表示区间 x,y->标记区间
if (l > y || r < x) {
return 0;
}
if (x<=l&&r<=y) {
//printf("range %d %d:%lld\n",l,r,tree[y]);
return tree[k];
}
if (tag[k]) { //如果当前节点是被标记的节点
if(l^r){ //不是叶节点
Down(k, l, r, tag[k]); //下传标记
}
tag[k]=0; //清空该点标记
}
ll ans = 0;
int mid = (l + r) >> 1; //二分区间
if(x<=mid) {
ans += Find(k << 1, l, mid, x, y); //左子树
}
if(y>mid) {
ans += Find(k << 1 | 1, mid + 1, r, x, y); //右子树
}
return ans;
}
int main() {
//freopen("P3372_8.in","r",stdin);
//freopen("data.out","w",stdout);
ios::sync_with_stdio(false);//输入输出加速
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> num[i];
}
Build(1, 1, n);//建树
for (int i = 1; i <= m; i++) {
int cmd;
cin >> cmd;
if (cmd == 1) {//区间修改
int x, y;
ll k;
cin >> x >> y >> k;
Lazy_tag(1, 1, n, x, y, k);
}
else if (cmd == 2) {//区间求和
int x, y;
cin >> x >> y;
cout<<Find(1, 1, n, x, y)<<endl;
}
}
return 0;
}