题目链接: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;
}