线段树模板总结
本文只总结一下线段树的易错点(毕竟这玩意我早就会了)
重点:
- 线段树tag的意义
- 线段树开4倍空间的意义
注释里都有。
update
函数任务清单
pushdown
- 递归到子区间
pushup
query
函数任务清单
pushdown
- 递归到子区间
//线段树不能严格开4倍空间, 要开大于等于MAXN的最小2的幂次的4倍空间
//如:MAXN = 10000, 则要开 16384*4 的空间。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 550005;
ll sum[MAXN], tag[MAXN];
//tag表示:这个区间已处理过,但它的子区间还没有。
void pushdown(ll o, ll l, ll r) {
ll ls, rs, mid;
if(tag[o] == 0) return;
ls = o << 1; rs = ls + 1; mid = (l+r) >> 1;
sum[ls] += tag[o] * (mid - l + 1);
sum[rs] += tag[o] * (r - mid);
tag[ls] += tag[o]; tag[rs] += tag[o];
tag[o] = 0;
}
ll query(ll o, ll l, ll r, ll ql, ll qr) {
pushdown(o, l, r);
// printf("[%lld,%lld]: %lld\n", l, r, sum[o]);
if(ql <= l && r <= qr) {
return sum[o];
}
ll mid = (l+r)>>1, ret = 0, ls, rs;
ls = o << 1; rs = ls + 1;
if(ql <= mid) ret += query(ls, l, mid, ql, qr);
if(qr > mid) ret += query(rs, mid+1, r, ql, qr);
printf("%d %d %d ql=%d qr=%d ret= %lld\n", l, r, sum[o], ql, qr, ret);
return ret;
}
void update(ll o, ll l, ll r, ll ql, ll qr, ll v) {
pushdown(o, l, r);
ll mid = (l+r)>>1, ls, rs;
if(ql <= l && r <= qr) {
sum[o] += (r-l+1) * v;
tag[o] += v;
return;
}
ls = o << 1; rs = ls + 1;
if(ql <= mid) update(ls, l, mid, ql, qr, v);
if(qr > mid) update(rs, mid+1, r, ql, qr, v);
sum[o] = sum[ls] + sum[rs];
}
int a[MAXN], n, q;
signed main(){
#ifndef ONLINE_JUDGE
freopen(".in","r",stdin);
freopen(".out","w", stdout);
#endif
scanf("%d %d", &n, &q);
for(int i = 1; i <= n; i++) {
ll tmp;
scanf("%lld", &tmp);
update(1, 1, n, i, i, tmp);
}
for(int i = 1; i <= q; i++) {
int op, l, r, k;
scanf("%d%d%d",&op,&l,&r);
if(op == 1) {
scanf("%d", &k);
update(1, 1, n, l, r, k);
} else {
printf("%lld\n", query(1,1,n,l,r));
}
}
return 0;
}