线段树

关于区间修改的理解
首先明确给节点 p p p 打上懒标记的含义是:该节点曾经被修改,但其子节点尚未被更新。
所以当节点 p p p所代表的区间被包含时直接修改 s t [ p ] . s u m s st[p].sums st[p].sums,并给 p p p打上一个懒标记,并返回上一层(记做 x − 1 x-1 x1)。对于第 x − 1 x-1 x1层的节点 p p p来说,第 x x x层的节点 p p p是它的子节点,所以在函数末尾有 s t [ p ] . s u m s = s t [ p < < 1 ] . s u m s + s t [ p < < 1 ∣ 1 ] . s u m s st[p].sums=st[p<<1].sums+st[p<<1|1].sums st[p].sums=st[p<<1].sums+st[p<<11].sums来维护数据的正确性。
a s k ask ask函数中调用 s p r e a d spread spread函数是因为运行到 s p r e a d spread spread说明节点 p p p所代表的区间并不包含在 [ l , r ] [l,r] [l,r]之间,要去它的子节点中寻找,但根据懒标记的定义,子节点的值尚未被修改,所以要调用 s p r e a d spread spread来维护数据的正确性。
c h a n g e change change函数同理。

经典例题
线段树(模板)
动态求连续区间和
数列区间最大值

C o d e : Code: Code:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include 
using namespace std;
#define MaxN 100010
//#define MOD 998244353
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define endl '\n'
#define LL long long
#define PII pair
#define rint register int 
#define ULL unsigned long long
const int MOD=1e9+7;
//int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
//int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
template<class T> inline void read(T& x){
    x=0;int f=0;char ch=getchar();
    while( !isdigit(ch) ) f|=( ch == '-' ) , ch=getchar();
    while( isdigit(ch) )  x = ( x<<1 ) + ( x<<3 ) + ( ch^48 ) , ch=getchar();
    x = f ? -x : x;
}
template<class T> inline void print(T x){
	if ( x < 0 ) { putchar('-'); x = -x; }
	if ( x >= 10 ) print( x / 10 );
	putchar(x % 10 + '0');
}
struct Node{
	int l,r,add;
	LL sums;
}st[MaxN*4];
int A[MaxN];
//建树
void build(int p,int l,int r){
	st[p].l=l;st[p].r=r;
	if( l == r ){
		st[p].sums=1ll*A[l];
		return ;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	st[p].sums=st[p<<1].sums+st[p<<1|1].sums;
}
//延迟标记
void spread(int p){
	if( st[p].add ){
		st[p<<1].sums+=1ll*st[p].add*(st[p<<1].r-st[p<<1].l+1);//更新左子树
		st[p<<1|1].sums+=1ll*st[p].add*(st[p<<1|1].r-st[p<<1|1].l+1);//更新右子树
		st[p<<1].add+=st[p].add;
		st[p<<1|1].add+=st[p].add;
		st[p].add=0;
	}
}
//区间修改
void change(int p,int l,int r,int d){
	if( l <= st[p].l && st[p].r <= r ){
		st[p].sums+=1ll*d*(st[p].r-st[p].l+1);
		st[p].add+=d;
		return ;
	}
	spread(p);
	int mid=st[p].l+st[p].r>>1;
	if( l <= mid ) change(p<<1,l,r,d);
	if( r > mid ) change(p<<1|1,l,r,d);
	st[p].sums=st[p<<1].sums+st[p<<1|1].sums;
}
//区间查询
LL ask(int p,int l,int r){
	if( l <= st[p].l && st[p].r <= r ){
		return st[p].sums;
	}
	spread(p);
	int mid=st[p].l+st[p].r>>1;
	LL ans=0;
	if( l <= mid ) ans+=ask(p<<1,l,r);
	if( r > mid ) ans+=ask(p<<1|1,l,r);
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m;
	cin>>n>>m;
	for(int i=1; i<=n; i++) cin>>A[i];
	build(1,1,n);
	while( m-- ){
		int sign;
		cin>>sign;
		if( sign == 1 ){
			int x,y,k;
			cin>>x>>y>>k;
			change(1,x,y,k);
		}
		else{
			int x,y;
			cin>>x>>y;
			cout<<ask(1,x,y)<<endl;
		}
	}
	return 0; 
}

你可能感兴趣的:(算法模板)