题目链接:http://hihocoder.com/problemset/problem/1078
线段树区间求和,区间更新。
#include <iostream> #include <cstdio> using namespace std; const int MAX_N = 1e5 + 5; int seg_n; int seg[MAX_N << 2]; // 标记数组 标记区间是否需要被更新 int flg[MAX_N << 2]; void init( int n_ ) { seg_n = 1; while ( seg_n < n_ ) seg_n <<= 1; } // 单点更新 void update( int k, int x ) { k += seg_n-1; int diff = x-seg[k]; seg[k] = x; while ( k > 0 ) { k = (k-1)/2; seg[k] += diff; } } void pushdown( int k, int l, int r ) { // 叶子节点 不再更新子节点 if ( r-l == 1 ) return; // 否则更新区间值 并标记区间 int mid = (l+r) >> 1; flg[2*k+1] = flg[k]; seg[2*k+1] = (mid-l)*flg[k]; flg[2*k+2] = flg[k]; seg[2*k+2] = (r-mid)*flg[k]; // 去除父区间标记 flg[k] = 0; } void pushup( int k ) { seg[k] = seg[2*k+1] + seg[2*k+2]; } // 区间更新 不是简单的遍历区间每个点进行单点更新 这样会 tle // 区间更新的思想是 添加一个标记数组 标记某个区间是否要被更新 // 更新时 并不直接更新叶子节点 而是先更新一次区间值 供查询时使用 // 如果查询时 需要查询该区间子区间的值时 再把这个更新落实下去 // 这样更新被延迟到了不得不更新的时候 // 有时候不需要再访问的时候 这次更新就被优化掉了 void update( int a, int b, int x, int k, int l, int r ) { if ( a >= r || b <= l ) return; if ( a <= l && b >= r ) { seg[k] = (r-l)*x; // [l,r] 对应区间节点 k 标记值x flg[k] = x; return ; } // 如果这个区间被标记了 并且接下来要使用更新后的值 则把这次更新落实下去 if ( flg[k] ) pushdown( k, l, r ); update( a, b, x, 2*k+1, l, (l+r)/2 ); update( a, b, x, 2*k+2, (l+r)/2, r ); // 更新两个子节点后 更新当前结点值 pushup(k); } int query( int a, int b, int k, int l, int r ) { if ( a >= r || b <= l ) return 0; if ( a <= l && b >= r ) return seg[k]; else { // 查询时也需要一次更新 但不需要 pushup if ( flg[k] ) pushdown( k, l, r ); return query(a,b,2*k+1,l,(l+r)/2) + query(a,b,2*k+2,(l+r)/2,r); } } int main() { int N, M; int op, l, r, x; scanf( "%d", &N ); init( N ); for ( int i = 0 ; i < N ; ++ i ) { scanf( "%d", &x ); update( i, x ); } // cout << seg[0] << endl; scanf("%d", &M); for ( int i = 0 ; i < M ; ++ i ) { scanf( "%d", &op ); if ( op == 1 ) { scanf( "%d%d%d", &l, &r, &x ); update( l-1, r, x, 0, 0, seg_n ); } else { scanf( "%d%d", &l, &r ); printf( "%d\n", query( l-1, r, 0, 0, seg_n ) ); } } return 0; }