[Luogu 3707] SDOI2017 相關分析

[Luogu 3707] SDOI2017 相關分析

<題目連接>node


前言

<big>ui

Capella 和 Frank 同樣愛好天文學。spa

她常在冬季的夜晚,如有所思地望着東北方上空的五邊形中,最爲耀眼的一個頂點。code

那一抹金黃曾帶給她多少美好的遐想!get

直到有一天,她作了這一題,並飽受線段樹的折磨。string

</big>it

概述

<big>io

這是一道區間操做的題目,解法有線段樹與分塊等。class

題主選擇線段樹進行講解(由於沒用分塊寫這題)。gc

</big>

準備與計算答案

<big>

首先要知道,線段樹須要維護的數值有哪些。

先來看題目中所給的公式。

$\bar x = \frac 1 {r-l+1} \sum_{i=l}^r x_i$

$\bar y = \frac 1 {r-l+1} \sum_{i=l}^r y_i$

$\hat a = \frac{\sum_{i=l}^r (x_i - \bar x)(y_i - \bar y)}{\sum_{i=l}^r (x_i - \bar x)^2}$

將線性迴歸方程的公式展開得($\sum_{i=l}^r$ 省略爲 $\sum$):

$\hat a = \frac{\sum (x_i y_i - \bar x y_i - \bar y x_i + \bar x \bar y)} {\sum (x_i^2 - 2\bar x x_i + \bar x^2)}$

$= \frac{\sum x_i y_i - \bar x \sum y_i - \bar y \sum x_i + (r-l+1) \bar x \bar y} {\sum x_i^2 - 2\bar x \sum x_i + (r-l+1) \bar x^2}$

將 $\bar x$、$\bar y$ 代入上式得:

$\hat a = \frac{\sum x_i y_i - \frac{\sum x_i \sum y_i}{r-l+1} - \frac{\sum x_i \sum y_i}{r-l+1} + (r-l+1) \frac{\sum x_i}{r-l+1} \cdot \frac{\sum y_i}{r-l+1}} {\sum x_i^2 - \frac{2(\sum x_i)^2}{r-l+1} + (r-l+1) (\frac{\sum x_i}{r-l+1})^2}$

$= \frac{\sum x_i y_i - \frac{\sum x_i \sum y_i}{r-l+1}} {\sum x_i^2 - \frac{(\sum x_i)^2}{r-l+1}}$

因而可知,線段樹須要維護的數值有 $\sum x_i^2$、$\sum x_i y_i$、$\sum x_i$ 以及 $\sum y_i$。

設 $v_0 = \sum x_i^2, v_1 = \sum x_i y_i, v_2 = \sum x_i, v_3 = \sum y_i$,

則最終計算出的 $\hat a = \frac{v_1 - \frac{v_2 v_3}{r-l+1}}{v_0 - \frac{v_2^2}{r-l+1}}$。

</big>

區間加操做

<big>

進行區間加操做時,設 $x$ 的變化量爲 $S$,$y$ 的變化量爲 $T$,則各個標記的下傳過程以下:

$\sum (x_i + S)^2 = \sum (x_i^2 + 2Sx_i + S^2)$
$= \sum x_i^2 + 2S \sum x_i + (r-l+1)S^2$

$\sum (x_i+S)(y_i+T) = \sum (x_i y_i + Sy_i+Tx_i+ST)$
$=\sum x_i y_i + S \sum y_i + T \sum x_i + (r-l+1)ST$

$\sum (x_i+S) = \sum x_i + (r-l+1)S$

$\sum (y_i+T) = \sum y_i +(r-l+1)T$

特別注意:因爲更新 $\sum x_i^2 $ 與 $\sum x_i y_i$ 時,須要用到更新前的 $\sum x_i$ 與 $\sum y_i$ 的值,因此應注意順序

</big>

區間修改操做

前置技能

$1^2 + 2^2 + \dots + n^2 = \frac {n(n+1)(2n+1)} 6$

轉化

<big>

試圖將區間修改操做轉化爲區間加操做。

對於 $i \in [l,r]$,$x_i$ 改成 $S+i$,$y_i$ 改成 $T+i$,

至關於將 $i \in [l,r]$ 的每一個 $x_i$ 與 $y_i$ 都改成 $i$ ,

$\sum x_i^2 = \sum y_i^2 = \sum i^2 = \frac {r(r+1)(2r+1)} 6 - \frac {l(l-1)(2l-1)} 6$(即 $\sum_{i=1}^r i^2 -\sum_{i=1}^{l-1} i^2$)

$\sum x_i = \sum y_i = \sum i = \frac {(r-l+1)(l+r)} 2$($r-l+1$ 爲區間元素個數,$\frac {l+r} 2$ 爲區間平均值)

而後清空Lazy Tag(不管區間曾經加了多少都沒有用,將被統一修改)。

</big>

維護操做

<big>

清空Lazy Tag後,對區間 $[l,r]$ 進行區間加操做,即:每一個 $x_i$ 加上 $S$,每一個 $y_i$ 加上 $T$。

更新過程與區間加操做徹底相同。

最後還要打上標記c,表示此區間的子區間須要區間修改操做。

</big>

注意事項

<big>

  • 必定記得下傳Lazy Tagc標記,我就是由於沒傳才調了一上午+一下午+半個晚上的。

  • 本題下傳標記部分較爲複雜,請必定注意順序

</big>

結束語

<big>

這道題有點像《HAOI2012 高速公路》啊。

線段樹這種東西,多推一推也就熟悉啦。

加油,各位隊友;加油,本身。

</big>

#include <cstdio>
#include <cstring>
const int MAXN=100010;
double x[MAXN],y[MAXN];
int n,m;
class SegmentTree
{
    public:
        void Build(int i,int l,int r)
        {
            s[i]=node(l,r);
            if(l==r)
            {
                s[i].v[0]=x[l]*x[l],s[i].v[1]=x[l]*y[r],s[i].v[2]=x[l],s[i].v[3]=y[r];
                return;
            }
            int j=i<<1,mid=l+r>>1;
            Build(j,l,mid),Build(j|1,mid+1,r),PushUp(i);
        }
        void Add(int i,int l,int r,double S,double T)
        {
            if(l==s[i].l && r==s[i].r)
            {
                AddModify(i,S,T);
                return;
            }
            if(s[i].l^s[i].r)
                PushDown(i);
            int j=i<<1,mid=s[i].l+s[i].r>>1;
            if(r<=mid)
                Add(j,l,r,S,T);
            else if(l>mid)
                Add(j|1,l,r,S,T);
            else
                Add(j,l,mid,S,T),Add(j|1,mid+1,r,S,T);
            PushUp(i);
        }
        void Change(int i,int l,int r,double S,double T)
        {
            if(l==s[i].l && r==s[i].r)
            {
                ChangeModify(i),AddModify(i,S,T);
                return;
            }
            if(s[i].l^s[i].r)
                PushDown(i);
            int j=i<<1,mid=s[i].l+s[i].r>>1;
            if(r<=mid)
                Change(j,l,r,S,T);
            else if(l>mid)
                Change(j|1,l,r,S,T);
            else
                Change(j,l,mid,S,T),Change(j|1,mid+1,r,S,T);
            PushUp(i);
        }
        double Ans(double l,double r)
        {
            double size=r-l+1;
            memset(ans,0,sizeof ans);
            Sum(1,l,r);
            return (ans[1]-ans[2]*ans[3]/size)/(ans[0]-ans[2]*ans[2]/size);
        }
    private:
        double ans[4];
        struct node
        {
            bool c;
            double S,T,v[4];
            int l,r;
            node(int _l=0,int _r=0)
            {
                l=_l,r=_r,c=S=T=0;
            }
        }s[MAXN<<2];
        double Calc(double i)
        {
            return i*(i+1)*(2*i+1)/6;
        }
        void AddModify(int i,double S,double T)
        {
            double size=double(s[i].r-s[i].l+1);
            s[i].v[0]+=S*S*size+2*S*s[i].v[2];
            s[i].v[1]+=S*T*size+S*s[i].v[3]+T*s[i].v[2];
            s[i].v[2]+=S*size;
            s[i].v[3]+=T*size;
            s[i].S+=S,s[i].T+=T;
        }
        void ChangeModify(int i)
        {
            double l=double(s[i].l),r=double(s[i].r);
            s[i].v[0]=s[i].v[1]=Calc(r)-Calc(l-1),s[i].v[2]=s[i].v[3]=(r-l+1)*(l+r)/2,s[i].c=1,s[i].S=s[i].T=0;
        }
        void PushUp(int i)
        {
            int l=i<<1,r=l|1;
            for(int k=0;k<4;++k)
                s[i].v[k]=s[l].v[k]+s[r].v[k];
        }
        void PushDown(int i)
        {
            int l=i<<1,r=l|1;
            if(s[i].c)
                ChangeModify(l),ChangeModify(r);
            AddModify(l,s[i].S,s[i].T),AddModify(r,s[i].S,s[i].T);
            s[i].c=s[i].S=s[i].T=0;
        }
        void Sum(int i,int l,int r)
        {
            if(l==s[i].l && r==s[i].r)
            {
                for(int k=0;k<4;++k)
                    ans[k]+=s[i].v[k];
                return;
            }
            if(s[i].l^s[i].r)
                PushDown(i);
            int j=i<<1,mid=s[i].l+s[i].r>>1;
            if(r<=mid)
                Sum(j,l,r);
            else if(l>mid)
                Sum(j|1,l,r);
            else
                Sum(j,l,mid),Sum(j|1,mid+1,r);
        }
}SgT;
int main(int argc,char *argv[])
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%lf",&x[i]);
    for(int i=1;i<=n;++i)
        scanf("%lf",&y[i]);
    SgT.Build(1,1,n);
    for(int i=1,opt,l,r;i<=m;++i)
    {
        double S,T;
        scanf("%d %d %d",&opt,&l,&r);
        switch(opt)
        {
            case 1:
                printf("%.10lf\n",SgT.Ans(l,r));
                break;
            case 2:
                scanf("%lf %lf",&S,&T);
                SgT.Add(1,l,r,S,T);
                break;
            case 3:
                scanf("%lf %lf",&S,&T);
                SgT.Change(1,l,r,S,T);
                break;
        }
    }
    return 0;
}

謝謝閱讀。

相關文章
相關標籤/搜索