线段树

题目1:(带懒标记的线段树)

给定一个长度为 <span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="mi">NN 的数列 <span id="MathJax-Span-5" class="mrow"><span id="MathJax-Span-6" class="mi">AA,以及 <span id="MathJax-Span-8" class="mrow"><span id="MathJax-Span-9" class="mi">MM 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 <span id="MathJax-Span-11" class="mrow"><span id="MathJax-Span-12" class="mi">A<span id="MathJax-Span-13" class="mo">[<span id="MathJax-Span-14" class="mi">l<span id="MathJax-Span-15" class="mo">]<span id="MathJax-Span-16" class="mo">,<span id="MathJax-Span-17" class="mi">A<span id="MathJax-Span-18" class="mo">[<span id="MathJax-Span-19" class="mi">l<span id="MathJax-Span-20" class="mo">+<span id="MathJax-Span-21" class="mn">1<span id="MathJax-Span-22" class="mo">]<span id="MathJax-Span-23" class="mo">,<span id="MathJax-Span-24" class="mo">&hellip;<span id="MathJax-Span-25" class="mo">,<span id="MathJax-Span-26" class="mi">A<span id="MathJax-Span-27" class="mo">[<span id="MathJax-Span-28" class="mi">r<span id="MathJax-Span-29" class="mo">]A[l],A[l+1],…,A[r] 都加上 <span id="MathJax-Span-31" class="mrow"><span id="MathJax-Span-32" class="mi">dd。
  2. Q l r,表示询问数列中第 <span id="MathJax-Span-34" class="mrow"><span id="MathJax-Span-35" class="mi">l<span id="MathJax-Span-36" class="mo">&sim;<span id="MathJax-Span-37" class="mi">rl∼r 个数的和。

对于每个询问,输出一个整数表示答案。

题目来源:https://www.acwing.com/problem/content/244/

#include<bits/stdc++.h>
#define LL long long 
using namespace std;

const int N = 1e5+1;
int n,m,w[N];

struct node{
	int l,r;
	LL sum,add;
}tr[4*N];

void pushup(int u){
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void build(int u,int l,int r){
	if(l==r)
		tr[u]={l,r,w[l],0};
	else{
		tr[u]={l,r};
		int mid = l + r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
}

void pushdown(int u){
	if(tr[u].add){
		node &left=tr[u<<1],&right=tr[u<<1|1];
		tr[u<<1].add+=tr[u].add,left.sum+=(LL)(left.r-left.l+1)*tr[u].add;
		tr[u<<1|1].add+=tr[u].add,right.sum+=(LL)(right.r-right.l+1)*tr[u].add;
		tr[u].add=0;
	}
}
void modify(int u,int l,int r,int d){
    if(tr[u].l>= l and tr[u].r<=r){
        tr[u].sum+=(LL)(tr[u].r-tr[u].l+1)*d;
        tr[u].add+=d;
    }
    else{
        int mid = tr[u].l +tr[u].r>>1;
        pushdown(u);//为保证下面pushup正确更新区间,需要下传懒标记
        if(l<=mid)modify(u<<1,l,r,d);
        if(r>mid)modify(u<<1|1,l,r,d);
        pushup(u);//只有区间被不完全覆盖才需要pushup比如1-10,其中1-5不操作,5-10加20
    }//因为若完全覆盖则在pushdown时就可以更新整个子区间并下传标记
}

LL query(int u,int l,int r){
    if(tr[u].l>= l and tr[u].r<=r)return tr[u].sum;
    int mid=tr[u].l + tr[u].r >> 1;
    LL sum=0; 
    pushdown(u);
    if(l<=mid)sum=query(u<<1,l,r);
    if(r>mid)sum+=query(u<<1|1,l,r);
    pushup(u);//这行不必要,modify结束后区间已完全被懒标记覆盖,故不需要pushup
    
    return sum;
}
int main(){
	char op;
	int l,r,d;
	cin>>n>>m;
	for(int i = 1 ; i <= n ; i ++ )
		cin>>w[i];
	build(1,1,n);
	while(m--){
		cin>>op>>l>>r;
		if(op=='Q'){
			cout<<query(1,l,r)<<endl;
		}
		else{
			cin>>d;
			modify(1,l,r,d);
		}
	}
	return 0;
}

  注意点:1.modify和query中递归都是从l到r,开头的if中的区间都是区间范围在l到r之间,都需要开始pushdown,结束pushup;

      2.mid是区间中的mid,tr[u].r+tr[u].r>>1;

      3.注意不要忘记在main函数中建树build,建树过程中初始化节点tr【u】={xxx},最后记得pushup;

posted @ 2022-10-26 20:14  picapicac  阅读(21)  评论(0)    收藏  举报