P3707 [SDOI2017] 相关分析

题意

有两个序列 \(x,y\),修改有两种。

  1. \((l,r,S,T)\):对于所有 \(i\in[l,r]\)\(x_i\) 加上 \(S\)\(y_i\) 加上 \(T\)
  2. \((l,r,S,T)\):对于所有 \(i\in[l,r]\)\(x_i\) 变成 \(S+i\)\(y_i\) 变成 \(T+o\)

查询:令 \(\displaystyle\bar x=\frac1{r-l+1}sum_{i-l}^rx_i,\bar y=\frac1{r-l+1}sum_{i-l}^ry_i\),求\({\displaystyle\sum_{i=L} ^R (x_i-\overline{x})(y_i-\overline{y}) \over \displaystyle\sum _{i=L} ^R (x_i -\overline{x})^2}\)

思路

\(\bar x\)\(\bar y\) 是好求的,用个线段树维护即可。
考虑求 \(\sum_{i=L}^R(x_i-\bar x)(y_i-\bar y)\)。把祂拆开。

\[\begin{array}{lr} =\sum_{i=L}^R(x_iy_i-\bar xy_i-\bar{y}x_i+\bar{x}\bar{y})\\ =\sum_{i=L}^Rx_iy_i-\bar x\sum_{i=L}^Ry_i-\bar y\sum_{i=L}^Rx_i+\bar x\bar y(R-L+1) \end{array} \]

都是好维护的。
考虑求 \(\sum_{i=L}^R(x_i-\bar x)^2\)。把祂拆开。

\[\begin{array}{lr} =\sum_{i=L}^R(x_i^2+\bar x^2-2x_i\bar x)\\ =\sum_{i=L}^Rx_i^2+(R-L+1)\bar x^2-2\bar x\sum_{i=L}^Rx_i \end{array} \]

依然很好维护。
也就是说,线段树中需要维护 \(x,y,x^2,xy\)

代码

/*
Luogu P3707 [SDOI2017] 相关分析
2026-04-09
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL __int128
const int maxn=100010;
int n,m;
LL sum2[maxn];
LL calc(int l,int r){return 1ll*(l+r)*(r-l+1)/2;}
LL calc2(int l,int r){if(l==0) return sum2[r];return sum2[r]-sum2[l-1];}
class Segment_Tree{
private:
    struct node{
        LL x,y,x2,xy,lanS,lanT,lan2S,lan2T;
        node(){lan2S=lan2T=-1;}
    }t[maxn*8];
    void add_tag1(int u,LL S,LL T,int l,int r){
        LL sumx=t[u].x,sumy=t[u].y;
        t[u].x+=(r-l+1)*S,t[u].y+=(r-l+1)*T;
        t[u].xy+=sumx*T+sumy*S+(r-l+1)*S*T;
        t[u].x2+=2*sumx*S+(r-l+1)*S*S;
        t[u].lanS+=S,t[u].lanT+=T;
    }
    void add_tag2(int u,LL S,LL T,int l,int r){
        t[u].lanS=t[u].lanT=0;
        t[u].x=(r-l+1)*S+calc(l,r),t[u].y=(r-l+1)*T+calc(l,r);
        t[u].xy=calc2(l,r)+calc(l,r)*(S+T)+(r-l+1)*S*T;
        t[u].x2=calc2(l,r)+2*calc(l,r)*S+S*S*(r-l+1);
        t[u].lan2S=S,t[u].lan2T=T;
    }
    void down(int u,int l,int r){
        if(l==r) return ;
        int mid=l+r>>1;
        if(~t[u].lan2S) add_tag2(u<<1,t[u].lan2S,t[u].lan2T,l,mid);
        add_tag1(u<<1,t[u].lanS,t[u].lanT,l,mid);
        if(~t[u].lan2S) add_tag2(u<<1|1,t[u].lan2S,t[u].lan2T,mid+1,r);
        add_tag1(u<<1|1,t[u].lanS,t[u].lanT,mid+1,r);
        t[u].lan2S=t[u].lan2T=-1,t[u].lanS=t[u].lanT=0;
    }
    void push_up(int u){
        t[u].x=t[u<<1].x+t[u<<1|1].x;
        t[u].y=t[u<<1].y+t[u<<1|1].y;
        t[u].x2=t[u<<1].x2+t[u<<1|1].x2;
        t[u].xy=t[u<<1].xy+t[u<<1|1].xy;
    }
    void update1(int u,int l,int r,int ll,int rr,LL S,LL T){
        if(l>rr||r<ll) return ;
        if(ll<=l&&r<=rr){
            add_tag1(u,S,T,l,r);
            return ;
        }
        down(u,l,r);
        int mid=l+r>>1;
        update1(u<<1,l,mid,ll,rr,S,T),update1(u<<1|1,mid+1,r,ll,rr,S,T);
        push_up(u);
    }
    void update2(int u,int l,int r,int ll,int rr,LL S,LL T){
        if(l>rr||r<ll) return ;
        if(ll<=l&&r<=rr){
            add_tag2(u,S,T,l,r);
            return ;
        }
        down(u,l,r);
        int mid=l+r>>1;
        update2(u<<1,l,mid,ll,rr,S,T),update2(u<<1|1,mid+1,r,ll,rr,S,T);
        push_up(u);
    }
    tuple<LL,LL,LL,LL>add(tuple<LL,LL,LL,LL>a,tuple<LL,LL,LL,LL>b){return{get<0>(a)+get<0>(b),get<1>(a)+get<1>(b),get<2>(a)+get<2>(b),get<3>(a)+get<3>(b)};}
    tuple<LL,LL,LL,LL>query(int u,int l,int r,int ll,int rr){
        if(l>rr||r<ll) return{0,0,0,0};
        if(ll<=l&&r<=rr) return{t[u].x,t[u].y,t[u].x2,t[u].xy};
        down(u,l,r);
        int mid=l+r>>1;
        return add(query(u<<1,l,mid,ll,rr),query(u<<1|1,mid+1,r,ll,rr));
    }
public:
    void update1(int l,int r,int S,int T){update1(1,1,n,l,r,S,T);}
    void update2(int l,int r,int S,int T){update2(1,1,n,l,r,S,T);}
    double query(int l,int r){
        auto[x,y,x2,xy]=query(1,1,n,l,r);
        double xp=x*1.0/(r-l+1),yp=y*1.0/(r-l+1);
        double up=xy-xp*y-yp*x+xp*yp*(r-l+1),dn=x2+(r-l+1)*xp*xp-2*xp*x;
        return up/dn;
    }
}t;
signed main(){
    sum2[0]=0;
    for(int i=1;i<=100000;i++) sum2[i]=sum2[i-1]+1ll*i*i;
    read(n,m);
    for(int i=1;i<=n;i++){
        int S;read(S);
        t.update1(i,i,S,0);
    }
    for(int i=1;i<=n;i++){
        int T;read(T);
        t.update1(i,i,0,T);
    }
    for(int i=1;i<=m;i++){
        int op,l,r,S,T;read(op,l,r);
        if(op==1) printf("%.10lf\n",t.query(l,r));
        if(op==2){
            read(S,T);
            t.update1(l,r,S,T);
        }
        if(op==3){
            read(S,T);
            t.update2(l,r,S,T);
        }
    }
    return 0;
}
posted @ 2026-04-09 19:42  Link-Cut_Trees  阅读(13)  评论(0)    收藏  举报