题目链接:https://nanti.jisuanke.com/t/31460
题目思路:很明显的一个线段树题目,但是要求的是a[l]*len+a[l+1]*(len-1)+a[l+2]*(len-2)+......+a[r],所以根据这个推得以下公式:
可以试着推一下这个公式。所以我们维护两个线段树,sum1保存正常的数组区间和,sum2保存a[i]*i的数组的区间和,
当查询区间为[l,r]时,答案就是sum2[l,r]-(n-r)*sum1[l,r]
代码和思路参考于:https://blog.csdn.net/ccsu_cat/article/details/82594389
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
/*
维护两个线段树sum1和sum2,sum1维护正常区间和,sum2维护(n-i+1)*a[i]的区间和
答案就是smu2[l,r]-(n-r)sum1[l,r]
公式可以自己推一下
*/
const int maxn=1e5+10;
typedef long long ll;
ll sum1[maxn*4],sum2[maxn*4],a[maxn];//线段树开4倍空间
int n;
void build(int o,int l,int r)
{
if(l==r)
{
sum1[o]=a[l];
sum2[o]=a[l]*(n-l+1);
return;
}
int ls=o*2,rs=o*2+1,m=(l+r)/2;//左右子节点和中间节点
build(ls,l,m);
build(rs,m+1,r);
sum1[o]=sum1[ls]+sum1[rs];
sum2[o]=sum2[ls]+sum2[rs];
}
//查询sum1
ll query1(int o,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return sum1[o];
int ls=2*o,rs=2*o+1,m=(l+r)/2;
ll res=0;
if(ql<=m)res+=query1(ls,l,m,ql,qr);
if(qr>m)res+=query1(rs,m+1,r,ql,qr);
return res;
}
//查询sum2
ll query2(int o,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return sum2[o];
int ls=2*o,rs=2*o+1,m=(l+r)/2;
ll res=0;
if(ql<=m)res+=query2(ls,l,m,ql,qr);
if(qr>m)res+=query2(rs,m+1,r,ql,qr);
return res;
}
//更新
void update(int o,int l,int r,int k,ll v)
{
if(l==r)
{
sum1[o]=v;
sum2[o]=v*(n-l+1);
return;
}
int ls=o*2,rs=o*2+1,m=(l+r)/2;
if(k<=m)update(ls,l,m,k,v);
else update(rs,m+1,r,k,v);
sum1[o]=sum1[ls]+sum1[rs];
sum2[o]=sum2[ls]+sum2[rs];
}
int main()
{
int q;
while(scanf("%d%d",&n,&q)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
build(1,1,n);
while(q--)
{
int x;
scanf("%d",&x);
if(x==1)
{
int l,r;
scanf("%d%d",&l,&r);
ll temp1=query1(1,1,n,l,r);
ll temp2=query2(1,1,n,l,r);
ll temp3=n-r;
printf("%lld\n",temp2-temp3*temp1);
}
else
{
int k;
ll v;
scanf("%d%lld",&k,&v);
update(1,1,n,k,v);
}
}
}
return 0;
}