#线段树,懒标记#JZOJ 4231 寻找神格 洛谷 2122 还教室 洛谷 1471 方差

题目

满足区间查找和区间维护方差


分析

看到这两个操作,我第一时间想到了线段树,但是貌似常数有点大,于是我又尝(zuo)试(si)打zkw线段树,改了一天也没改出来,第二天又改回线段树,结果WA50分,晚上又尝(zuo)试(si)用树状数组,然后就一脸懵逼,最后只好找回老本,线段树,发现没开long long,然后就在今晚9点多改A了,讲一下主要的思路
首先方差公式 1 r − l + 1 ∑ i = l r ( a [ i ] − a . a v e ) 2 \frac{1}{r-l+1}\sum_{i=l}^r(a[i]-a.ave)^2 rl+11i=lr(a[i]a.ave)2
化简得到 1 r − l + 1 ∑ i = l r ( a [ i ] 2 − 2 a [ i ] a . a v e + a . a v e 2 ) \frac{1}{r-l+1}\sum_{i=l}^r(a[i]^2-2a[i]a.ave+a.ave^2) rl+11i=lr(a[i]22a[i]a.ave+a.ave2)
也就是 1 r − l + 1 ∑ i = l r a [ i ] 2 − ∑ i = l r 2 a [ i ] a . a v e + ∑ i = l r a . a v e 2 \frac{1}{r-l+1}\sum_{i=l}^ra[i]^2-\sum_{i=l}^r2a[i]a.ave+\sum_{i=l}^ra.ave^2 rl+11i=lra[i]2i=lr2a[i]a.ave+i=lra.ave2
= 1 r − l + 1 ∑ i = l r a [ i ] 2 − a . a v e ( ∑ i = l r 2 a [ i ] + ∑ i = l r a . a v e ) =\frac{1}{r-l+1}\sum_{i=l}^ra[i]^2-a.ave(\sum_{i=l}^r2a[i]+\sum_{i=l}^ra.ave) =rl+11i=lra[i]2a.ave(i=lr2a[i]+i=lra.ave)
= 1 r − l + 1 ∑ i = l r a [ i ] 2 − a . a v e ( ∑ i = l r 2 a [ i ] + ∑ i = l r a [ i ] ) =\frac{1}{r-l+1}\sum_{i=l}^ra[i]^2-a.ave(\sum_{i=l}^r2a[i]+\sum_{i=l}^ra[i]) =rl+11i=lra[i]2a.ave(i=lr2a[i]+i=lra[i])
为了精度问题,分子分母同乘 ( r − l + 1 ) (r-l+1) (rl+1)可以得到
= 1 ( r − l + 1 ) 2 ( r − l + 1 ) ∑ i = l r a [ i ] 2 − ( ∑ i = l r a [ i ] ) 2 =\frac{1}{(r-l+1)^2}(r-l+1)\sum_{i=l}^ra[i]^2-(\sum_{i=l}^ra[i])^2 =(rl+1)21(rl+1)i=lra[i]2(i=lra[i])2
那么可以发现只需要维护区间和以及区间平方和即可,那如何修改平方和?
∑ i = l r ( a [ i ] + t ) 2 = ∑ i = l r a [ i ] 2 + ∑ i = l r 2 a [ i ] t + ( r − l + 1 ) t 2 \sum_{i=l}^r(a[i]+t)^2=\sum_{i=l}^ra[i]^2+\sum_{i=l}^r2a[i]t+(r-l+1)t^2 i=lr(a[i]+t)2=i=lra[i]2+i=lr2a[i]t+(rl+1)t2
只需要维护 ∑ i = l r 2 a [ i ] t + ( r − l + 1 ) t 2 \sum_{i=l}^r2a[i]t+(r-l+1)t^2 i=lr2a[i]t+(rl+1)t2即可


代码(还教室)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=100010; int n,m;
typedef long long ll;
ll w1[N<<2],w2[N<<2],laz[N<<2],a[N];
inline ll iut(){
    rr ll ans=0,f=1; rr char c=getchar();
    while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans*f;
}
inline void print(ll ans){
    if (ans<0) ans=-ans,putchar('-');
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline void pup(int k){w1[k]=w1[k<<1]+w1[k<<1|1],w2[k]=w2[k<<1]+w2[k<<1|1];}
inline void pdown(int k,int l,int r,ll t){
    laz[k]+=t;
    w2[k]+=2*t*w1[k]+t*t*(r-l+1);
    w1[k]+=t*(r-l+1);
}
inline void build(int k,int l,int r){
    if (l==r) {w1[k]=a[l],w2[k]=a[l]*a[l]; return;}
    rr int mid=(l+r)>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    pup(k);
}
inline void pr(int k,int l,int r){
    rr int mid=(l+r)>>1;
    pdown(k<<1,l,mid,laz[k]);
    pdown(k<<1|1,mid+1,r,laz[k]);
    laz[k]=0;
}
inline void update(int k,int l,int r,int x,int y,ll t){
    if (x<=l&&r<=y) {pdown(k,l,r,t); return;}
    pr(k,l,r); rr int mid=(l+r)>>1;
    if (x<=mid) update(k<<1,l,mid,x,y,t);
    if (y>mid) update(k<<1|1,mid+1,r,x,y,t);
    pup(k);	
}
inline ll query1(int k,int l,int r,int x,int y){
    if (x<=l&&r<=y) return w1[k]; rr ll ans=0;
    pr(k,l,r); rr int mid=(l+r)>>1;
    if (x<=mid) ans+=query1(k<<1,l,mid,x,y);
    if (y>mid) ans+=query1(k<<1|1,mid+1,r,x,y);
    return ans;
}
inline ll query2(int k,int l,int r,int x,int y){
    if (x<=l&&r<=y) return w2[k]; rr ll ans=0;
    pr(k,l,r); rr int mid=(l+r)>>1;
    if (x<=mid) ans+=query2(k<<1,l,mid,x,y);
    if (y>mid) ans+=query2(k<<1|1,mid+1,r,x,y);
    return ans;	
}
signed main(){
    n=iut(); m=iut();
    for (rr int i=1;i<=n;++i) a[i]=iut();
    build(1,1,n);
    while (m--){
        rr int q=iut(),x=iut(),y=iut();
        if (q==1) update(1,1,n,x,y,iut());
        else if (q==2){
            rr ll t=query1(1,1,n,x,y);
            rr ll g=y-x+1,h=__gcd(t,g);
            print(t/h); putchar('/');
            print(g/h); putchar(10);
        }
        else{
            rr ll t1=query1(1,1,n,x,y),t2=query2(1,1,n,x,y);
            rr ll t=t2*(y-x+1)-t1*t1;
            rr ll g=(long long)(y-x+1)*(y-x+1),h=__gcd(t,g);
            print(t/h); putchar('/');
            print(g/h); putchar(10); 
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值