jzoj4231-寻找神格【线段树,数学】
正題
題目大意
4個操作
單點修改,區間修改,區間求和,區間求方差
方差為:∑(xi?ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi??ave)2?
aveaveave為平均值
解題思路
我們將方差的式子分解一下
∑(xi?ave)2n\frac{\sum(x_i-ave)^2}{n}n∑(xi??ave)2?
∑(xi2?2?x?ave+ave2)n\frac{\sum(x_i^2-2*x*ave+ave^2)}{n}n∑(xi2??2?x?ave+ave2)?
(∑xi2)?2?(∑xi)?ave+n?ave2n\frac{(\sum x_i^2)-2*(\sum x_i)*ave+n*ave^2}{n}n(∑xi2?)?2?(∑xi?)?ave+n?ave2?
然后因為aveaveave可以用區間和求,所以我們只需要多維護一個區間平方和
之后我們考慮如何快速計算,對于一個區間所有數都加上valvalval,之后的平方和
∑(xi+val)2\sum (x_i+val)^2∑(xi?+val)2
我們將其分解
∑(xi2+2?xi?val+val2)\sum (x_i^2+2*x_i*val+val^2)∑(xi2?+2?xi??val+val2)
(∑xi2)+(∑xi)?2?val+n?val2(\sum x_i^2)+(\sum x_i)*2*val+n*val^2(∑xi2?)+(∑xi?)?2?val+n?val2
然后(∑xi2)(\sum x_i^2)(∑xi2?)和(∑xi)(\sum x_i)(∑xi?)我們都知道,所以可以直接計算之后的平方和。
codecodecode
#include<cstdio> #define N 100010 #define ll long long #define lodu long double using namespace std; struct tree_node{ll l,r,w,ww,lazy; }t[N<<2]; ll n,q; lodu sum_f; void build(ll k,ll l,ll r)//建樹 {t[k].l=l;t[k].r=r;if(l==r){scanf("%lld",&t[k].w);t[k].ww=t[k].w*t[k].w;return;}ll mid=(l+r)>>1;build(k*2,l,mid);build(k*2+1,mid+1,r);t[k].w=t[k*2].w+t[k*2+1].w;t[k].ww=t[k*2].ww+t[k*2+1].ww; } void get_fan(ll k,ll z)//求區間加了z之后的區間平方和 {t[k].ww=t[k].ww+t[k].w*2*z+z*z*(t[k].r-t[k].l+1);} void downdata(ll k){if(t[k].lazy==0) return;get_fan(k*2,t[k].lazy);get_fan(k*2+1,t[k].lazy); t[k*2].w+=t[k].lazy*(t[k*2].r-t[k*2].l+1);t[k*2+1].w+=t[k].lazy*(t[k*2+1].r-t[k*2+1].l+1);t[k*2].lazy+=t[k].lazy;t[k*2+1].lazy+=t[k].lazy;t[k].lazy=0; }//下傳lazy標記 void change(ll k,ll l,ll r,ll z)//區間修改 {if(t[k].l==l&&t[k].r==r){get_fan(k,z);t[k].lazy+=z;t[k].w+=z*(r-l+1);return;}downdata(k);if(r<=t[k*2].r) change(k*2,l,r,z);else if(l>=t[k*2+1].l) change(k*2+1,l,r,z);else change(k*2,l,t[k*2].r,z),change(k*2+1,t[k*2+1].l,r,z);t[k].ww=t[k*2].ww+t[k*2+1].ww;t[k].w=t[k*2].w+t[k*2+1].w; } ll get_sum(ll k,ll l,ll r)//區間求和和平方和 {if(t[k].l==l&&t[k].r==r){sum_f+=t[k].ww;return t[k].w;}downdata(k);if(r<=t[k*2].r) return get_sum(k*2,l,r);if(l>=t[k*2+1].l) return get_sum(k*2+1,l,r);return get_sum(k*2,l,t[k*2].r)+get_sum(k*2+1,t[k*2+1].l,r); } int main() {//freopen("data.in","r",stdin);//freopen("data.out","w",stdout);scanf("%lld%lld",&n,&q);build(1,1,n);for(ll i=1;i<=q;i++){ll ts,a,b,z;scanf("%lld%lld%lld",&ts,&a,&b);if(ts==0) change(1,a,a,b);else if(ts==1){scanf("%lld",&z);change(1,a,b,z);}else if(ts==2) sum_f=0,printf("%lld\n",get_sum(1,a,b));else{sum_f=0;lodu ave,ans,sum=get_sum(1,a,b),n=b-a+1;ave=sum/n;ans=sum_f+ave*ave*n-sum*ave*2; printf("%0.10Lf\n",ans/n);}} }總結
以上是生活随笔為你收集整理的jzoj4231-寻找神格【线段树,数学】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 0元起有效降低笔记本温度和风扇噪音怎么降
- 下一篇: 硬核•安卓手机通用root攻略