【题目传送门】
一眼分块能做,然后就来开始做。狠狠地做。
构造
我们考虑根号分治,对于每个块我们开:当前的数值和 \(sum1\),当前的数值平方的和 \(sum2\),当前的数值的 \(\operatorname {lazy}\) 数组。
这道题最阴险的地方是它的输入不是全部整数,但是样例给的是正整数,然后我给我数组全部开的 \(\operatorname {int}\) 类型,听取 WA 声一片。
修改
- 我们对于散块直接暴力处理,主要是不要忘了更新 \(sum1、sum2\)。
- 我们对于整块的处理就更加直接,直接叠加到 \(\operatorname{lazy}\) 里面就好。
平均值查询
通过 \(sum1+tag\times len\) 直接可以得出。
方差查询
我们要开始推式子了。
我们求方差的式子是:
\[\begin{equation}
\begin{split}
len=r-l+1,\\
s^2&=\frac{\sum^{r}_{i=l}(\ x_i\ -\bar{\ x\ })^2}{len} \\&=\frac{\sum^{r}_{i=l}(x_i^2\ -\ 2\cdot\bar{\ x\ }\cdot x_i\ +\ \bar{\ x\ }^2)}{len}\\&=\frac{\sum^{r}_{i=l}x_i^2}{len}\ -2\cdot\bar{\ x\ }\cdot\frac{\sum^{r}_{i=l}x_i}{len}\ +\ \bar{\ x\ }\cdot\frac{\sum^{r}_{i=l}\bar{\ x\ }}{len}\\&=\frac{\sum^{r}_{i=l}x_i^2}{len}\ -2\cdot\bar{\ x\ }^2\ +\ \bar{\ x\ }^2\\&=\frac{\sum^{r}_{i=l}x_i^2}{len}\ -\bar{\ x\ }^2
\end{split}
\end{equation}
\]
也就是我们要知道某个区间的方差只需要知道改区间的平方和以及平均数就好了,显然,平均数可以直接套用操作 \(1\) 前面我们写的代码。
我们由于这个 \(lazy\_tag\) (下称 \(tag\))存在,显然不是很好直接处理这个平方和的式子,但是不用 \(tag\) 又不行,那我们只能继续推式子寻找破局之道了(这里的 \(tag\) 设为 \(k\)):
\[\begin{equation}
\begin{split}
sum^2&=\sum_{i=l}^{r}(x_i+k)\\&=\sum^{r}_{i=l}x_i^2\ +\ 2k\cdot\sum^r_{i=l}x_i\ +\ nk^2
\end{split}
\end{equation}
\]
秒杀,下面是代码。
\(\mathcal{CODE}\)
#include<bits/stdc++.h>
using namespace std;
#define itn int
#define reaD read
#define int long long
inline void read(int &x){x=0;int p=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')p=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=p;
}
void write(int x){if(x<0){putchar('-');x*=-1;}if(x<10){putchar(x+48);return;}write(x/10);putchar(x%10+48);
}
const int N=1e5+1000,M=800;
int n,m,pos[N],L[M],R[M],cnt,len;
double tag[M],sum1[M],sum2[M],a[N];void init(){len=(int)sqrt(n);cnt=(n-1)/len+1;for(int i=1;i<=n;i++){pos[i]=(i-1)/len+1;}for(int i=1;i<=cnt;i++){L[i]=(i-1)*len+1;R[i]=min(n,i*len);for(int j=L[i];j<=R[i];j++){sum1[i]+=a[j];sum2[i]+=a[j]*a[j];}}
}void catter(int l,int r,double c){for(int i=l;i<=r;i++){sum2[pos[l]]+=(a[i]+c)*(a[i]+c)-a[i]*a[i];a[i]+=c;}sum1[pos[l]]+=1.0*(r-l+1)*c;
}void pre_block(int id,double c){tag[id]+=c;
}void update(int l,int r,double c){if(pos[l]==pos[r]){catter(l,r,c);return;}catter(l,R[pos[l]],c);catter(L[pos[r]],r,c);for(int i=pos[l]+1;i<pos[r];i++){pre_block(i,c);}return;
}double query_catter1(int l,itn r){double res=0;for(int i=l;i<=r;i++){res+=a[i];}res+=(r-l+1)*tag[pos[l]];return res;
}double query_block1(int id){double res=0;res+=sum1[id];res+=(R[id]-L[id]+1)*tag[id];return res;
}double query1(int l,int r){if(pos[l]==pos[r]){double res=query_catter1(l,r);return 1.0*res/(r-l+1);}double res=0;res+=query_catter1(l,R[pos[l]]);res+=query_catter1(L[pos[r]],r);for(int i=pos[l]+1;i<pos[r];i++){res+=query_block1(i);}return res/(r-l+1);
}double query_catter2(int l,int r){double res=0;for(int i=l;i<=r;i++){res+=(a[i]+tag[pos[l]])*(a[i]+tag[pos[l]]);}return res;
}double query_block2(int id){double res=0;res+=sum2[id];res+=2*tag[id]*sum1[id];res+=(1.0*R[id]-L[id]+1)*tag[id]*tag[id];return res;
}double query2(int l,int r){if(pos[l]==pos[r]){double res1=query_catter2(l,r);double res2=query_catter1(l,r);// cout<<"res1:"<<res1<<" res2:"<<res2<<endl;double ans=1.0*res1/(r-l+1);ans-=(res2/(r-l+1))*(res2/(r-l+1));return ans;}double res1=0,res2=0;res1+=query_catter2(l,R[pos[l]]);res1+=query_catter2(L[pos[r]],r);res2+=query_catter1(l,R[pos[l]]);res2+=query_catter1(L[pos[r]],r);for(int i=pos[l]+1;i<pos[r];i++){res1+=query_block2(i);res2+=query_block1(i);}double ans=res1/(r-l+1);ans-=(res2/(r-l+1))*(res2/(r-l+1));// cout<<"res1:"<<res1<<" res2:"<<res2<<endl;return ans;
}main(void){// ios::sync_with_stdio(false);// cin.tie(nullptr);// cout.tie(nullptr);read(n);read(m);for(int i=1;i<=n;i++){cin>>a[i];}// for(int i=1;i<=n;i++){// printf("%.2lf\n",a[i]);// }init();for(int i=1,top,l,r;i<=m;i++){read(top);double c;if(top==1){read(l);read(r);cin>>c;update(l,r,c);}if(top==2){read(l);read(r);printf("%.4lf\n",query1(l,r));}if(top==3){read(l);read(r);printf("%.4lf\n",query2(l,r));}}
}