BZOJ 4821 [Sdoi2017]相关分析

Description
Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度、颜色等等,进而估算出
星星的距离,半径等等。Frank不仅喜欢观测,还喜欢分析观测到的数据。他经常分析两个参数之间(比如亮度和
半径)是否存在某种关系。现在Frank要分析参数X与Y之间的关系。他有n组观测数据,第i组观测数据记录了x_i和
y_i。他需要一下几种操作1 L,R:用直线拟合第L组到底R组观测数据。用xx表示这些观测数据中x的平均数,用yy
表示这些观测数据中y的平均数,即
xx=Σx_i/(R-L+1)(L<=i<=R)
yy=Σy_i/(R-L+1)(L<=i<=R)
如果直线方程是y=ax+b,那么a应当这样计算:
a=(Σ(x_i-xx)(y_i-yy))/(Σ(x_i-xx)(x_i-xx)) (L<=i<=R)
你需要帮助Frank计算a。
2 L,R,S,T:
Frank发现测量数据第L组到底R组数据有误差,对每个i满足L <= i <= R,x_i需要加上S,y_i需要加上T。
3 L,R,S,T:
Frank发现第L组到第R组数据需要修改,对于每个i满足L <= i <= R,x_i需要修改为(S+i),y_i需要修改为(T+i)。

Input
第一行两个数n,m,表示观测数据组数和操作次数。
接下来一行n个数,第i个数是x_i。
接下来一行n个数,第i个数是y_i。
接下来m行,表示操作,格式见题目描述。
1<=n,m<=10^5,0<=|S|,|T|,|x_i|,|y_i|<=10^5
保证1操作不会出现分母为0的情况。

Output
对于每个1操作,输出一行,表示直线斜率a。
选手输出与标准输出的绝对误差不超过10^-5即为正确。

Sample Input
3 5

1 2 3

1 2 3

1 1 3

2 2 3 -3 2

1 1 2

3 1 2 2 1

1 1 3
Sample Output
1.0000000000

-1.5000000000

-0.6153846154
HINT

Source

对于询问,十分难受,将它化简。
ri=l(xix¯)(yiy¯)ri=l(xix¯)2

ri=lxiyix¯ri=lyiy¯ri=lxi(rl+1)x¯y¯ri=lx2i2x¯ri=lxi+(rl+1)x¯2

len=rl+1
x¯=ri=lxilen
y¯=ri=lyilen

lenri=lxiyiri=lxiri=lyilenri=lx2i(ri=lxi)2

可以看出只需要维护 xiyi , xi , yi , x2i

做出一棵线段树,2操作较简单,3操作可以看作先覆盖,再进行2操作。

注意标记下传的时候,先检查是否有覆盖标记,如果有将加法标记清除。

更新覆盖标记时,需要运用数列求和的姿势,注意要开double,防止溢出。

#include<iostream>
#include<cstdio>
#define lc num<<1
#define rc num<<1|1
using namespace std;
const int N=100005;
int n,m;
double xx[N],yy[N];
struct segtree
{
    int l,r,len;
    double sum[5],tagx,tagy;
    bool chg;
    //1 x 2 y 3 x2 4 xy
}st[4*N];
void update1(int num,double s,double t)
{
    st[num].sum[3]+=2*s*st[num].sum[1]+st[num].len*s*s;
    st[num].sum[4]+=s*st[num].sum[2]+t*st[num].sum[1]+st[num].len*s*t;
    st[num].sum[1]+=st[num].len*s;
    st[num].sum[2]+=st[num].len*t;
    st[num].tagx+=s,st[num].tagy+=t;
}
void update2(int num)
{
    st[num].sum[2]=st[num].sum[1]=((double)(st[num].r+st[num].l)*st[num].len)/2;
    st[num].sum[3]=st[num].sum[4]=((double)st[num].r*(st[num].r+1)*(2*st[num].r+1)-(double)(st[num].l-1)*st[num].l*(2*st[num].l-1))/6;
    st[num].chg=1;
    st[num].tagx=st[num].tagy=0;
}
void pushup(int num)
{
    for(int i=1;i<=4;i++)
        st[num].sum[i]=st[lc].sum[i]+st[rc].sum[i];
}
void pushdown(int num)
{
    if(st[num].chg)
    {
        if(st[num].l!=st[num].r)
            update2(lc),update2(rc);
        st[num].chg=0;
    }
    if(st[num].tagx!=0||st[num].tagy!=0)//区间加法 
    {
        if(st[num].l!=st[num].r)
            update1(lc,st[num].tagx,st[num].tagy),update1(rc,st[num].tagx,st[num].tagy);
        st[num].tagx=st[num].tagy=0;
    }
}
void build(int num,int l,int r)
{
    st[num].l=l,st[num].r=r,st[num].len=r-l+1;
    if(l==r)
    {
        st[num].sum[1]=xx[l],st[num].sum[2]=yy[l];
        st[num].sum[3]=xx[l]*xx[l],st[num].sum[4]=xx[l]*yy[l];
        return ;
    }
    int mid=l+r>>1;
    build(lc,l,mid),build(rc,mid+1,r);
    pushup(num);
}
void add(int num,int x,int y,double s,double t)//区间加法 
{
    if(st[num].l>=x&&st[num].r<=y)
    {
        update1(num,s,t);
        return ;
    }
    pushdown(num);
    if(x<=st[lc].r)
        add(lc,x,y,s,t);
    if(y>=st[rc].l)
        add(rc,x,y,s,t);
    pushup(num);
}
void change(int num,int x,int y)//将第i个数改为i 
{
    if(st[num].l>=x&&st[num].r<=y)
    {
        update2(num);
        return ;
    }
    pushdown(num);
    if(x<=st[lc].r)
        change(lc,x,y);
    if(y>=st[rc].l)
        change(rc,x,y);
    pushup(num);
}
double query(int num,int x,int y,int z)
{
    if(st[num].l>=x&&st[num].r<=y)
        return st[num].sum[z];
    pushdown(num);
    double res=0;
    if(x<=st[lc].r)
        res+=query(lc,x,y,z);
    if(y>=st[rc].l)
        res+=query(rc,x,y,z);
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lf",&xx[i]);
    for(int i=1;i<=n;i++)
        scanf("%lf",&yy[i]);
    build(1,1,n);
    int k,l,r;
    double x,y;
    while(m--)
    {
        scanf("%d%d%d",&k,&l,&r);
        if(k==1)
        {
            double res1=query(1,l,r,1),res2=query(1,l,r,2),res3=query(1,l,r,3),res4=query(1,l,r,4);
            int len=r-l+1;
            printf("%f\n",(len*res4-res1*res2)/(len*res3-res1*res1));
        }
        else
        {
            scanf("%lf%lf",&x,&y);
            if(k==3)
                change(1,l,r);
            add(1,l,r,x,y);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值