本文只总结一下线段树的易错点(毕竟这玩意我早就会了)
重点:
- 线段树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; }