题目大意:
一个长度为的数列,要求支持个操作:
- ,表示将位置的数字增加。
- ,表示中的数字全部加。
- ,表示询问之间的数字和。
- ,表示询问之间的数字方差。
思路:
,考虑分块。(个人感觉分块比线段树好打)。
对于操作,很明显是需要在每个块内维护块内和。所以用表示块中的数字和。
对于操作,考虑将方差用完全平方公式拆开。
先不考虑。
拆开得
去括号+整理得
整理得
将带入得
那么就再维护每个块的平方和就可以在内求出答案了。
维护倒是不难,熟用完全平方公式注意细节即可。
代码:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=100010;
const int M=350;
int n,Q,T,L[M],R[M],pos[N];
ll ans[M],add[M],sum[M],a[N],Read,f,x,y,z;
char ch;
ll read()
{
Read=0;
f=1;
ch=getchar();
while (ch<'0'||ch>'9')
{
if (ch=='-') f=-f;
ch=getchar();
}
while (ch>='0'&&ch<='9')
Read=(Read<<3)+(Read<<1)+ch-48,ch=getchar();
return Read*f;
}
void change(int l,int r,ll z) //修改
{
int q=pos[l],p=pos[r];
if (q==p) //一个块暴力修改
{
for (register int i=l;i<=r;i++)
ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
return;
}
for (register int i=l;i<=R[q];i++)
ans[q]+=2*(a[i]+add[q])*z+z*z,a[i]+=z,sum[q]+=z;
for (register int i=L[p];i<=r;i++) //分开
ans[p]+=2*(a[i]+add[p])*z+z*z,a[i]+=z,sum[p]+=z;
for (register int i=q+1;i<p;i++) //lazy修改
add[i]+=z,ans[i]+=2*sum[i]*z+z*z*(R[i]-L[i]+1),sum[i]+=z*(R[i]-L[i]+1);
}
ll ask1(int l,int r) //询问和
{
int q=pos[l],p=pos[r];
ll s=0;
if (q==p)
{
for (register int i=l;i<=r;i++) s+=(ll)(a[i]+add[q]); //暴力求和
return s;
}
for (register int i=l;i<=R[q];i++) s+=(ll)(a[i]+add[q]);
for (register int i=L[p];i<=r;i++) s+=(ll)(a[i]+add[p]);
for (register int i=q+1;i<p;i++) s+=sum[i];
return s;
}
ld ask2(int l,int r) //询问方差
{
int q=pos[l],p=pos[r];
ll Ans=0,Sum=0;
if (q==p)
{
for (register int i=l;i<=r;i++) //暴力
Sum+=a[i]+add[q];
ld Ave=(ld)Sum/(ld)(r-l+1),s=0.0;
for (register int i=l;i<=r;i++)
s+=((ld)a[i]+(ld)add[q]-Ave)*((ld)a[i]+(ld)add[q]-Ave);
return (ld)s/(ld)(r-l+1);
}
for (register int i=l;i<=R[q];i++) //左边
{
Ans+=(a[i]+add[q])*(a[i]+add[q]);
Sum+=a[i]+add[q];
}
for (register int i=L[p];i<=r;i++) //右边
{
Ans+=(a[i]+add[p])*(a[i]+add[p]);
Sum+=a[i]+add[p];
}
for (register int i=q+1;i<p;i++) //中间直接利用lazy
{
Ans+=ans[i];
Sum+=sum[i];
}
ld Ave=(ld)Sum/(ld)(r-l+1);
return ((ld)Ans-2.0*(ld)Sum*Ave+(ld)(r-l+1)*Ave*Ave)/(ld)(r-l+1);
//完全平方公式输出
}
int main()
{
n=read(),Q=read();
for (register int i=1;i<=n;i++)
a[i]=read();
T=sqrt(n);
if (T*T<n) T++;
for (register int i=1;i<=T;i++)
{
L[i]=R[i-1]+1;
R[i]=min(i*T,n);
for (register int j=L[i];j<=R[i];j++)
{
pos[j]=i;
ans[i]+=a[j]*a[j];
sum[i]+=a[j];
}
}
while (Q--)
{
x=read();
if (x==0)
{
x=read(),y=read();
change(x,x,y);
}
else if (x==1)
{
x=read(),y=read(),z=read();
change(x,y,z);
}
else if (x==2)
{
x=read(),y=read();
printf("%lld
",ask1(x,y));
}
else if (x==3)
{
x=read(),y=read();
printf("%0.10Lf
",ask2(x,y));
}
}
return 0;
}