维护平方和
题目:长度为n的数列
两种操作:1为区间所有的数加一个数
2为求区间的平方和
线段树的核心是维护lazy和tree
lazy是存操作(维护操作)
tree是存结果(维护结果)
此题建完树的结果为 a*a+b*b+c*c
操作后的结果 (a+A)*(a+A)+(b+A)*(b+A)+(c+A)*(c+A)
= (a*a+b*b+c*c) + 3*A*A + 2* (a+b+c) *A
|| ||
\/ \/
平方和(未操作前的结果) 和
==> 操后结果=操前结果+len*(操作数)*(操作数)+2*(朴素和)*操作数
所以不仅要维护操后结果(平方和),还要维护朴素和
维护tree
可以比较操前结果和操后结果的异同
再判断要维护几个tree和哪些东西#include<bits/stdc++.h>
using namespace std;
#define ll long long
int const N=1e5+7;
int n,m;
int a[N];
int ly[N<<2];
struct L{
int a,b;
}tr[N<<2];
void bulid(int p,int l,int r){
if(l==r){ tr[p].a=a[l]; tr[p].b=a[l]*a[l];return ; }
int mid=(l+r) >> 1;
bulid(p*2,l,mid);
bulid(p*2+1,mid+1,r);
tr[p].a = tr[p*2].a + tr[p*2+1].a ;
tr[p].b =tr[p*2].b + tr[p*2+1].b;
}
void pushdown(int p,int len){
tr[p*2].b += 2*tr[p*2].a*ly[p] + (len-len/2)*ly[p]*ly[p];
tr[p*2].a += ly[p]*(len-len/2);
tr[p*2+1].b += 2*tr[p*2+1].a*ly[p] + len/2*ly[p]*ly[p];
tr[p*2+1].a += ly[p]*len/2;
ly[p*2] += ly[p]; ly[p*2+1] += ly[p];
ly[p] = 0;
}
void add(int p,int l,int r,int x,int y,int k){
if(x<=l&&r<=y){
tr[p].b += 2*tr[p].a*k + (r-l+1)*k*k;
tr[p].a +=(r-l+1)*k;
ly[p]+=k;
return ;
}
pushdown(p,r-l+1);
int mid=(l+r) >> 1;
if(x<=mid) add(p*2,l,mid,x,y,k);
if(y>=mid+1) add(p*2+1,mid+1,r,x,y,k);
tr[p].a = tr[p*2].a + tr[p*2+1].a ;
tr[p].b = tr[p*2].b + tr[p*2+1].b ;
}
int ask(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tr[p].b ;
}
pushdown(p,r-l+1);
int mid = (l+r) >> 1;
int res=0;
if(x<=mid) res+=ask(p*2,l,mid,x,y);
if(mid+1<=y) res+=ask(p*2+1,mid+1,r,x,y);
return res;
}
int main(){
cin >> n >> m;
for(int i=1;i<=n;++i){
cin >> a[i];
}
bulid(1,1,n);
while(m--){
int op,x,y,k;
cin >> op >> x >> y;
if(op==1){
cin >> k;
add(1,1,n,x,y,k);
}
else cout << ask(1,1,n,x,y) << "\n";
}
return 0;
}线段树和数状数组经典例题 文章被收录于专栏
线段树和数状数组经典例题
嘉士伯公司氛围 714人发布