Luogu P3707 [SDOI2017]相关分析
题意
给定两个长度为 \(n\) 的序列 \(x,y\),有 \(m\) 次操作,每次操作形如以下三种:
-
1 l r
:求出 \((x_l,y_l),\cdots,(x_r,y_r)\) 的回归方程的斜率。 -
2 l r S T
:将满足 \(l\leq i\leq r\) 的 \(x_i\) 加上 \(S\),\(y_i\) 加上 \(T\)。 -
3 l r S T
:将满足 \(l\leq i\leq r\) 的 \(x_i\) 改为 \(S+i\),\(y_i\) 改为 \(T+i\)。
其中对于数据 \((x_1,y_1),\cdots (x_n,y_n)\) 的回归方程的斜率为
\[\hat{a}=\frac{\sum\limits_{i=1}^{n}(x_i-\overline{x})(y_i-\overline{y})}{\sum\limits_{i=1}^{n}(x_i-\overline{x})^2}
\]
\(\texttt{Data Range:}1\leq n,m\leq 10^5\)。
题解
写了两个小时,线段树懒标记部分写错都不知道自己是怎么死的。
mmp 这题怎么这么难写
考虑先推一下式子,强行展开有
\[\hat{a}=\frac{\sum\limits_{i=1}^{n}x_iy_i-\overline{x}\sum\limits_{i=1}^{n}y_i-\overline{y}\sum\limits_{i=1}^{n}x_i+n\overline{x}\overline{y}}{\sum\limits_{i=1}^{n}x_i^2-2\overline{x}\sum\limits_{i=1}^{n}x_i+n\overline{x}^2}
\]
考虑将 \(\overline{x}\) 和 \(\overline{y}\) 拆一下并且合并一下同类项得到
\[\hat{a}=\frac{\sum\limits_{i=1}^{n}x_iy_i-\frac{1}{n}\sum\limits_{i=1}^{n}x_i\sum\limits_{i=1}^{n}y_i}{\sum\limits_{i=1}^{n}x_i^2-\frac{1}{n}\sum\limits_{i=1}^{n}x_i}
\]
到这里维护的东西就很清晰了,考虑怎么来维护。一次的东西很平凡,考虑二次的。
\[\sum\limits_{i=1}^{n}(x_i+S)(y_i+T)=\sum\limits_{i=1}^{n}x_iy_i+S\sum\limits_{i=1}^{n}y_i+T\sum\limits_{i=1}^{n}x_i+nST
\]
然后就没了,同样的道理得到
\[\sum\limits_{i=1}^{n}(x_i+S)^2=\sum\limits_{i=1}^{n}x_i^2+2S\sum\limits_{i=1}^{n}x_i+nS^2
\]
这样就可以直接做 \(2\) 操作了。对于 \(3\) 操作的话可以先考虑强行赋值然后再区间加,赋值这一部分就是基础的求和公式运用,然后就没了。
一个注意的点是区间赋值递归到某个区间时要把这个区间的加法懒标记清零(我这个 sb 就是因为这个调了 1h 的)
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
typedef long double db;
const ll MAXN=2e5+51;
struct SegmentTree{
ll l,r,tag;
db sxx,sxy,sx,sy,tagx,tagy;
};
SegmentTree tree[MAXN<<2];
ll n,qcnt,op,l,r,len;
SegmentTree p,g;
db s,t;
db x[MAXN],y[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline db sum2(ll x)
{
return 1.0*x*(x+1)*(2*x+1)/6;
}
#define ls node<<1
#define rs (node<<1)|1
inline void update(ll node)
{
tree[node].sxx=tree[ls].sxx+tree[rs].sxx;
tree[node].sxy=tree[ls].sxy+tree[rs].sxy;
tree[node].sx=tree[ls].sx+tree[rs].sx;
tree[node].sy=tree[ls].sy+tree[rs].sy;
}
inline void create(ll l,ll r,ll node)
{
tree[node]=(SegmentTree){l,r};
if(l==r)
{
tree[node].sx=x[l],tree[node].sy=y[l];
return (void)(tree[node].sxx=x[l]*x[l],tree[node].sxy=x[l]*y[l]);
}
ll mid=(tree[node].l+tree[node].r)>>1;
create(l,mid,ls),create(mid+1,r,rs),update(node);
}
inline void spread(ll node)
{
ll lenl=tree[ls].r-tree[ls].l+1,lenr=tree[rs].r-tree[rs].l+1;
if(tree[node].tag)
{
tree[ls].sx=tree[ls].sy=lenl*tree[ls].l+lenl*(lenl-1)/2.0;
tree[rs].sx=tree[rs].sy=lenr*tree[rs].l+lenr*(lenr-1)/2.0;
tree[ls].sxx=tree[ls].sxy=sum2(tree[ls].r)-sum2(tree[ls].l-1);
tree[rs].sxx=tree[rs].sxy=sum2(tree[rs].r)-sum2(tree[rs].l-1);
tree[ls].tag=tree[rs].tag=1,tree[node].tag=0;
tree[ls].tagx=tree[ls].tagy=tree[rs].tagx=tree[rs].tagy=0;
}
if(tree[node].tagx!=0||tree[node].tagy!=0)
{
db u=tree[node].tagx,v=tree[node].tagy;
tree[ls].sxx+=2*u*tree[ls].sx+lenl*u*u;
tree[rs].sxx+=2*u*tree[rs].sx+lenr*u*u;
tree[ls].sxy+=u*tree[ls].sy+v*tree[ls].sx+lenl*u*v;
tree[rs].sxy+=u*tree[rs].sy+v*tree[rs].sx+lenr*u*v;
tree[ls].sx+=lenl*u,tree[ls].sy+=lenl*v;
tree[rs].sx+=lenr*u,tree[rs].sy+=lenr*v;
tree[ls].tagx+=u,tree[rs].tagx+=u,tree[node].tagx=0;
tree[ls].tagy+=v,tree[rs].tagy+=v,tree[node].tagy=0;
}
}
inline void add(ll l,ll r,db s,db t,ll node)
{
if(l<=tree[node].l&&r>=tree[node].r)
{
ll len=tree[node].r-tree[node].l+1;
tree[node].sxx+=2*s*tree[node].sx+len*s*s;
tree[node].sxy+=s*tree[node].sy+t*tree[node].sx+len*s*t;
tree[node].sx+=len*s,tree[node].sy+=len*t;
return (void)(tree[node].tagx+=s,tree[node].tagy+=t);
}
ll mid=(tree[node].l+tree[node].r)>>1;
spread(node),l<=mid?add(l,r,s,t,ls):(void)1,r>mid?add(l,r,s,t,rs):(void)1;
update(node);
}
inline void cover(ll l,ll r,ll node)
{
if(l<=tree[node].l&&r>=tree[node].r)
{
ll len=tree[node].r-tree[node].l+1;
tree[node].sxx=tree[node].sxy=sum2(tree[node].r)-sum2(tree[node].l-1);
tree[node].sx=tree[node].sy=tree[node].l*len+len*(len-1)/2.0;
return (void)(tree[node].tag=1,tree[node].tagx=tree[node].tagy=0);
}
ll mid=(tree[node].l+tree[node].r)>>1;
spread(node),l<=mid?cover(l,r,ls):(void)1,r>mid?cover(l,r,rs):(void)1;
update(node);
}
inline SegmentTree query(ll l,ll r,ll node)
{
SegmentTree u=g,v=g;
if(l<=tree[node].l&&r>=tree[node].r)
{
return tree[node];
}
ll mid=(tree[node].l+tree[node].r)>>1;
spread(node);
l<=mid?(void)(u=query(l,r,ls)):(void)1;
r>mid?(void)(v=query(l,r,rs)):(void)1;
return (SegmentTree){0,0,0,u.sxx+v.sxx,u.sxy+v.sxy,u.sx+v.sx,u.sy+v.sy};
}
int main()
{
n=read(),qcnt=read();
for(register int i=1;i<=n;i++)
{
scanf("%Lf",&x[i]);
}
for(register int i=1;i<=n;i++)
{
scanf("%Lf",&y[i]);
}
create(1,n,1);
for(register int i=1;i<=qcnt;i++)
{
op=read(),l=read(),r=read();
if(op==1)
{
p=query(l,r,1),len=r-l+1;
printf("%.10Lf\n",(p.sxy-p.sx*p.sy/len)/(p.sxx-p.sx*p.sx/len));
continue;
}
scanf("%Lf%Lf",&s,&t),op==3?cover(l,r,1):(void)1,add(l,r,s,t,1);
}
}