【题解】方差
这道题其实除了一些细节,就是个线段树板子题。虽然我WA了不知道多少次,只是因为不等号方向写反。
Copyright © 2019 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
算法分析
基本思路
以下是本蒟蒻的推导过程。
首先对方差公式进行变形:
\[\begin{aligned}
s^2&=\frac{\sum_{i=1}^{n}(x_i-\bar{x})^2}{n} \\ &=\frac{\sum_{i=1}^{n}(x_i^2+\bar{x}^2-2x_i\bar{x})}{n} \\ &=\frac{\sum_{i=1}^{n}x_i^2+\sum_{i=1}^{n}\bar{x}^2-2\bar{x}\sum_{i=1}^{n}x_i}{n}
\end{aligned}
\]
这是对于整个序列来说的方差,很显然,在对一个区间上求方差也是可以的。
可以发现,我们只要维护区间和,区间平方和即可,果断上线段树。
Copyright © 2019 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
但是对于区间加的操作,平方和又要怎么维护呢?请看下面:
\[\begin{aligned}
\sum_{i=l}^{r}(x_i+k)^2&=\sum_{i=l}^{r}(x_i^2+k^2+2x_ik) \\
&=\sum_{i=l}^{r}x_i^2+\sum_{i=l}^{r}k^2+2k\sum_{i=l}^{r}x_i \\
&=\sum_{i=l}^{r}x_i^2+2k\sum_{i=l}^{r}x_i+k^2(r-l+1)
\end{aligned}
\]
所以就可以写出只要的pushdown
函数:
inline void update(int x,int len,double v){ tree[x].add+=v; tree[x].sqr+=len*square(v)+2*v*tree[x].sum; // Warning 1 tree[x].sum+=len*v; } inline void pushdown(int x,int l,int r) { if(abs(tree[x].add)>eps){ // Warning 2 int mid=(l+r)>>1; update(ls(x),mid-l+1,tree[x].add); update(rs(x),r-mid,tree[x].add); tree[x].add=0.0; } }
浮点数比较
Copyright © 2019 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
浮点数是个很坑的东西,由于计算机架构的特殊原因,浮点数很不精确,可以自己运行一下下面的代码:
#include <bits/stdc++.h> using namespace std; int main(){ double x=3.14; printf("%.20lf",x); return 0; }
我的输出为\(3.14000000000000012434\),直接比较两个浮点数是很容易出锅的。
我们可以引入一个极小值\(\epsilon\),比如\(10^{-8}\),本题是可以接受的,然后判断两数差值的绝对值是否小于\(\epsilon\),如果小于,则两数相等,否则不等。
const double eps=1e-8; bool equal(double x,double y){ return abs(x-y)<eps; }
Copyright © 2019 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
代码实现
Talk is cheap, show you the code.
#include <bits/stdc++.h> #define FILE_IO true #define TIME_LIMIT 1000 using namespace std; struct node { double sum,sqr,add; }; const int maxn=100000+10; const double eps=1e-8; node tree[maxn<<2]; int n,m; double a[maxn]; inline int ls(int x) { return x<<1; } inline int rs(int x) { return x<<1|1; } inline void pushup(int x){ tree[x].sum=tree[ls(x)].sum+tree[rs(x)].sum; tree[x].sqr=tree[ls(x)].sqr+tree[rs(x)].sqr; } inline double square(double x) { return x*x; } inline void update(int x,int len,double v){ tree[x].add+=v; tree[x].sqr+=len*square(v)+2*v*tree[x].sum; tree[x].sum+=len*v; } inline void pushdown(int x,int l,int r) { if(abs(tree[x].add)>eps){ int mid=(l+r)>>1; update(ls(x),mid-l+1,tree[x].add); update(rs(x),r-mid,tree[x].add); tree[x].add=0.0; } } void build(int x,int l,int r){ if(l==r){ tree[x].sum=a[l]; tree[x].sqr=square(a[l]); return; } int mid=(l+r)>>1; build(ls(x),l,mid); build(rs(x),mid+1,r); pushup(x); } void modify(int x,int l,int r,int ml,int mr,double v){ if(ml<=l&&r<=mr){ update(x,r-l+1,v); return; } int mid=(l+r)>>1; pushdown(x,l,r); if(ml<=mid) modify(ls(x),l,mid,ml,mr,v); if(mid<mr) modify(rs(x),mid+1,r,ml,mr,v); pushup(x); } double querySum(int x,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr) return tree[x].sum; int mid=(l+r)>>1; double res=0.0; pushdown(x,l,r); if(ql<=mid) res+=querySum(ls(x),l,mid,ql,qr); if(mid<qr) res+=querySum(rs(x),mid+1,r,ql,qr); return res; } double querySquare(int x,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr) return tree[x].sqr; int mid=(l+r)>>1; double res=0.0; pushdown(x,l,r); if(ql<=mid) res+=querySquare(ls(x),l,mid,ql,qr); if(mid<qr) res+=querySquare(rs(x),mid+1,r,ql,qr); return res; } inline double queryAvarage(int l,int r){ return querySum(1,1,n,l,r)/(r-l+1); } inline double queryVariance(int l,int r){ double sum=querySum(1,1,n,l,r); double sqr=querySquare(1,1,n,l,r); double avg=sum/(r-l+1); double res=(sqr+(r-l+1)*square(avg)-2*avg*sum)/(r-l+1); return res; } int main(int argc, char *argv[]) { #if !defined(ONLINE_JUDGE) && FILE_IO freopen("project.in", "r", stdin); freopen("project.out", "w", stdout); clock_t stime = clock(); #endif // [ Codes ] //////////////////////// scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%lf",&a[i]); build(1,1,n); while(m--){ int op,x,y; double k; scanf("%d%d%d",&op,&x,&y); if(op==1){ scanf("%lf",&k); modify(1,1,n,x,y,k); }else if(op==2){ printf("%.4lf\n",queryAvarage(x,y)); }else{ printf("%.4lf\n",queryVariance(x,y)); } } // [ Codes ] //////////////////////// #if !defined(ONLINE_JUDGE) && FILE_IO clock_t etime = clock(); printf("\n----------------------------\n"); printf("Time : %dms\n", etime - stime); if (etime - stime >= TIME_LIMIT) { printf("<<< Warning >>> Time Limited Exceeded\n"); } #endif return 0; } // // ^ y // 1| y = sin x (0 < x < 2π) // - - - | - - - + - - - - - - - - - - - - - - // | + + // -------+---------------+---------------+----------> x // O| π + + 2π // - - - | - - - - - - - - - - - + - - - - - - // -1| // | //
Copyright © 2019 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
作者:ctjcalc
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步