Loading

洛谷P5142

本题化完简单式子然后直接上线段树即可。

Description

给定一个序列,对其进行单点修改和求区间方差的操作。

注意输出方差时要以分数取模形式输出。

Solution

单点修改不多说,来看求区间方差。

假设我们要求的是 \([x,y]\) 的方差,也就是求:

\[{\frac{1}{y-x+1}\sum_{i=x}^y}(a_i-\overline a)^2 \]

化简以下(为了方便设 \(d=y-x+1\)):

\({=\frac{1}{d}\sum_{i=x}^y(a_i^2-2a_i\overline a+\overline a^2)}\)

\({=\frac{1}{d}[\sum_{i=x}^y(a_i^2-2a_i\overline a)]+\overline a^2}\)

\({=\frac{1}{d}\sum_{i=x}^ya_i^2}-\frac{1}{d}\sum_{i=x}^ya_i\cdot 2\overline a+\overline a^2\)

\({=\frac{1}{d}\sum_{i=x}^ya_i^2}-2\overline a^2+\overline a^2\)

\({=\frac{1}{d}\sum_{i=x}^ya_i^2}-\overline a^2\)

所以根据推导过程可以看出,我们需要维护的是区间平方和以及区间和。

然后就上线段树板子,最后按上面的公式求值,分数换成逆元即可。

Code

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define maxn 510010
#define INF 0x3f3f3f3f
#define int long long
#define Mod 1000000007

using namespace std;

int n,m,a[maxn];

inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
    return s*w;
}

int quickpow(int x,int y){
    int ans=1;
    while(y){
        if(y&1)ans=(ans*x)%Mod;
        x=(x*x)%Mod;y>>=1;
    }
    return ans;
}

namespace Seg{
    #define ls x<<1
    #define rs x<<1|1
    #define f(x) x*x%Mod
    int sum[maxn],fsum[maxn];
    
    void pushup(int x){
        sum[x]=(sum[ls]+sum[rs])%Mod;
        fsum[x]=(fsum[ls]+fsum[rs])%Mod;
    }
    
    void build(int x,int l,int r){
        if(l==r){
            sum[x]=a[l]%Mod;
            fsum[x]=f(a[l]);
            return;
        }
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(x);
    }
    
    void update(int x,int l,int r,int pos,int val){
        if(l==r){
            sum[x]=val%Mod;
            fsum[x]=f(val);
            return;
        }
        int mid=l+r>>1;
        if(pos<=mid) update(ls,l,mid,pos,val);
        else update(rs,mid+1,r,pos,val);
        pushup(x);
    } 
    
    int query(int x,int l,int r,int L,int R){
        int ans=0;
        if(L<=l&&R>=r) return sum[x]%Mod;
        int mid=l+r>>1;
        if(L<=mid) ans=(ans+query(ls,l,mid,L,R))%Mod;
        if(R>=mid+1) ans=(ans+query(rs,mid+1,r,L,R))%Mod;
        return ans%Mod;
    }
    
    int query2(int x,int l,int r,int L,int R){
        int ans=0;
        if(L<=l&&R>=r) return fsum[x]%Mod;
        int mid=l+r>>1;
        if(L<=mid) ans=(ans+query2(ls,l,mid,L,R))%Mod;
        if(R>=mid+1) ans=(ans+query2(rs,mid+1,r,L,R))%Mod;
        return ans%Mod;
    }   
    
    int query3(int x,int l,int r,int L,int R,int val){
        int ans=0;
        if(L<=l&&R>=r) return fsum[x]%Mod;
        int mid=l+r>>1;
        if(L<=mid) ans=(ans+query2(ls,l,mid,L,R))%Mod;
        if(R>=mid+1) ans=(ans+query2(rs,mid+1,r,L,R))%Mod;
        return ans%Mod;
    }
}

signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) 
        a[i]=read();Seg::build(1,1,n);
    for(int i=1,opt,fir,sec;i<=m;i++){
        opt=read();fir=read();sec=read();
        if(opt==1) Seg::update(1,1,n,fir,sec);
        else{
            int ni=quickpow(sec-fir+1,Mod-2);
            int ave=Seg::query(1,1,n,fir,sec)*ni%Mod;
            int ans=Seg::query2(1,1,n,fir,sec)*ni%Mod-f(ave);
            printf("%lld\n",(ans%Mod+Mod)%Mod);
        }
    }
    return 0;
}
posted @ 2021-04-25 16:33  KnightL  阅读(40)  评论(0编辑  收藏  举报