输入:
5 6
1 2 3 4 5
1 1 5
2 1 5
3 1 2 1
4 1 3 2
1 1 4
2 2 3
输出:
15
55
16
41
思路: 核心:线段树
加法和求和省略不谈了(线段树基操,不会的话自己补一下~~
求平方和算个小套路吧:
假设区间每个元素加上x,求平方和
∑( ai + x)^2 == ∑(ai^2) +2 * x * ∑ai +区间长度 * (x^2) ,维护区间和即可,注意要先维护区间平方和再维护区间和,因为求区间平方和用到的区间和是修改前的区间和!
#include <bits/stdc++.h>
#define IOS std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
using namespace std;
typedef long long ll;
const int N=10010;
int a[N];
struct ty
{
ll sum,jia,cheng,ping; //和 + * 平方和
}tree[4*N];
void pushup(int p, int l, int r)
{
tree[p].sum =tree[2*p].sum + tree[2*p+1].sum;
tree[p].ping =tree[2*p].ping + tree[2*p+1].ping;
}
void pushdown(int p, int l, int r)
{
//先传乘法再传加法,乘标记需要改变加法标记
//(a+b)*c =ac+bc 括号展开
int mid = (l+r)>>1;
ll y=tree[p].cheng;
tree[2*p].jia *= y;
tree[2*p+1].jia *= y;
tree[2*p].cheng *= y;
tree[2*p+1].cheng *= y;
tree[2*p].sum *= y;
tree[2*p+1].sum *= y;
tree[2*p].ping *= y * y;
tree[2*p+1].ping *= y * y;
tree[p].cheng =1;//消除乘法标记!!!
ll x=tree[p].jia;
if( x )
{
tree[2*p].jia += x;
tree[2*p+1].jia += x;
//∑(ai+x)^2 == ∑ai^2 + 2x∑(ai^2) + 区间长度* (x^2)
//先维护平方和,再维护和!!
tree[2*p].ping += 2 * x * tree[2*p].sum +(mid-l+1) * x * x;
tree[2*p+1].ping += 2 * x * tree[2*p+1].sum +(r-(mid+1)+1) * x * x;
tree[2*p].sum += (mid-l+1) * x;
tree[2*p+1].sum += (r-(mid+1)+1) * x;
//标记一定记得清除!!
tree[p].jia=0;
}
}
void bulid(int p ,int l ,int r)
{
tree[p].jia=0;
tree[p].cheng=1;
if( l == r)
{
tree[p].sum = a[l];
tree[p].ping = a[l] * a[l];
return ;
}
int mid=(l + r) >> 1;
bulid(2*p,l,mid);
bulid(2*p+1,mid+1,r);
pushup(p,l,r);
}
void change(int p, int l, int r ,int a, int b, int num)
{
if(a <= l && r <= b)
{
tree[p].jia += num;
//先处理平方和,再处理和
tree[p].ping += 2 * num * tree[p].sum +(r-l+1) * num * num;
tree[p].sum += (r-l+1) * num;
return ;
}
if( tree[p].jia || tree[p].cheng!=1 ) pushdown(p,l,r);
int mid = (l+r) >> 1;
if( a <= mid ) change(2*p, l,mid,a,b,num);
if( b > mid ) change(2*p+1, mid+1, r,a,b,num);
pushup(p,l,r);
}
void cheng(int p, int l, int r ,int a, int b, int num)
{
if(a <= l && r <= b)
{
tree[p].cheng *= num;
tree[p].jia *= num;
tree[p].sum *= num; //区间每个元素乘num,即对区间和乘num
tree[p].ping *= num * num; //区间乘num方
return ;
}
if( tree[p].jia || tree[p].cheng!=1 ) pushdown(p,l,r);
int mid = (l+r) >> 1;
if( a <= mid ) cheng(2*p, l,mid,a,b,num);
if( b > mid ) cheng(2*p+1, mid+1, r,a,b,num);
pushup(p,l,r);
}
ll findsum(int p,int l,int r ,int a ,int b)
{
if(a <= l && r <= b)
{
return tree[p].sum;
}
if( tree[p].jia || tree[p].cheng!=1 ) pushdown(p,l,r);
int mid = (l+r) >> 1;
ll res=0;
if( a <= mid ) {res+=findsum(2*p, l,mid,a,b);}
if( b > mid ) {res+=findsum(2*p+1, mid+1, r,a,b);}
return res;
}
ll findping(int p,int l,int r ,int a ,int b)
{
if(a <= l && r <= b) return tree[p].ping;
if( tree[p].jia || tree[p].cheng!=1 ) pushdown(p,l,r);
int mid = (l+r) >> 1;
ll res=0;
if( a <= mid ) {res+=findping(2*p, l,mid,a,b);}
if( b > mid ) {res+=findping(2*p+1, mid+1, r,a,b);}
return res;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1 ;i<=n ;i++)
cin>>a[i];
//建树
bulid(1,1,n);
for(int i=1 ;i<=m ;i++)
{
int op,x,y,z;
cin>>op>>x>>y;
if( op == 1)
cout<<findsum(1,1,n,x,y)<<endl;
if( op == 2)
cout<<findping(1,1,n,x,y)<<endl;
if( op == 3)
{
cin>>z;
cheng(1,1,n,x,y,z);
}
if( op == 4)
{
cin>>z;
change(1,1,n,x,y,z);
}
}
}