LOJ #2005. 「SDOI2017」相关分析 线段树维护回归直线方程
题目描述#
FrankFrank 对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度、颜色等等,进而估算出星星的距离,半径等等。
FrankFrank 不仅喜欢观测,还喜欢分析观测到的数据。他经常分析两个参数之间(比如亮度和半径)是否存在某种关系。
现在 FrankFrank 要分析参数 XX 与 YY 之间的关系。他有 nn 组观测数据,第 ii 组观测数据记录了 xixi 和 yiyi。他需要一下几种操作
1 L,R:
用直线拟合第 L 组到第 R 组观测数据。用 ¯x 表示这些观测数据中 x 的平均数,用 ¯y 表示这些观测数据中 y 的平均数,即
¯x=1R−L+1∑Ri=Lxi
¯y=1R−L+1∑Ri=Lyi
如果直线方程是 y=ax+b,那么 a,b 应当这样计算:
a=∑Ri=L(xi−¯x)(yi−¯y)∑Ri=L(xi−¯x)2
你需要帮助 Frank 计算 a。
2 L,R,S,T:
Frank 发现测量数据第 L 组到第 R 组数据有误差,对每个 i 满足 L≤i≤R,xi 需要加上 S,yi 需要加上T。
3 L,R,S,T:
Frank发现第 L 组到第 R 组数据需要修改,对于每个 i 满足 L≤i≤R,xi需要修改为 (S+i),yi 需要修改为 (T+i)。
输入格式
第一行两个数 n,m,表示观测数据组数和操作次数。
接下来一行 n 个数,第 i 个数是 xi。
接下来一行 n 个数,第 i 个数是 yi。
接下来 m 行,表示操作,格式见题目描述。
输出格式#
对于每个 1 操作,输出一行,表示直线斜率 a。选手输出与标准输出的绝对误差或相对误差不超过 10−5 即为正确。
输入输出样例#
输入 #1#
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
输出 #1#
1.0000000000
-1.5000000000
-0.6153846154
说明/提示#
对于 20% 的数据 1≤n,m≤1000
另有 20% 的数据,没有 3 操作,且 2 操作中 S=0
另有 30% 的数据,没有 3 操作。
对于 100% 的数据,1≤n,m≤105,0≤|S|,|T|≤105,0≤|xi|,|yi|≤105
保证 1 操作不会出现分母为 0 的情况。
时间限制:1s
空间限制:128MB
分析#
把式子化简,就会得到
∑(xi−ˉx)(yi−ˉy)=∑(xiyi−xiˉy−yiˉxi+ˉxˉy)=∑xiyi−ˉy∑xi−ˉx∑yi+nˉxˉy=∑xiyi−nˉxˉy∑(xi−ˉx)2=∑(x2i+ˉx2−2xiˉx)=∑x2i+nˉx2−2ˉx∑xi=∑x2i−nˉx2
那么我们要维护的东西就是 xi、yi 和 xiyi
对于操作 2
∑xi→∑(xi+S)=∑xi+nS∑yi→∑(yi+T)=∑yi+nT∑x2i→∑(xi+S)2=∑x2i+nS2+2S∑xi∑xiyi→∑(xi+S)(yi+T)=∑xiyi+T∑xi+S∑yi+nST
对于操作 3
∑xi→∑(i+S)=s1+nS∑yi→∑(i+T)=s1+nT∑x2i→∑(i+S)2=s2+nS2+2Ss1∑xiyi→∑(i+S)(i+T)=s2+(T+S)s1+nST
其中 s1 是等差数列的求和公式 n(n+1)2
s2 是 i2 的前缀和 n(n+1)(2n+1)6
注意下放标记的时候只要有一个不为零就要下放
要先下放覆盖的标记,再下放加的标记
代码#
复制#include <cstdio>
#include <algorithm>
#include <cmath>
#define rg register
const int maxn = 1e5 + 5;
typedef double db;
int n, m;
db jlx[maxn], jly[maxn];
struct trr {
int l, r, siz;
db sumx, sumy, sumxx, sumxy, lazx, lazy, tagx, tagy;
trr() {
tagx = tagy = 1e18;
sumx = sumy = sumxx = sumxy = lazx = lazy = 0;
l = r = siz = 0;
}
} tr[maxn << 2];
db getsum1(int l, int r) { return (db)(r - l + 1.0) * (l + r) / 2.0; }
db getsum2(int r) { return (db)r * (r + 1.0) * (2.0 * r + 1.0) / 6.0; }
void push_up(int da) {
tr[da].sumx = tr[da << 1].sumx + tr[da << 1 | 1].sumx;
tr[da].sumy = tr[da << 1].sumy + tr[da << 1 | 1].sumy;
tr[da].sumxx = tr[da << 1].sumxx + tr[da << 1 | 1].sumxx;
tr[da].sumxy = tr[da << 1].sumxy + tr[da << 1 | 1].sumxy;
}
void push_down(int da) {
if (tr[da].tagx != 1e18 || tr[da].tagy != 1e18) {
tr[da << 1].tagx = tr[da].tagx;
tr[da << 1 | 1].tagx = tr[da].tagx;
tr[da << 1].tagy = tr[da].tagy;
tr[da << 1 | 1].tagy = tr[da].tagy;
tr[da << 1].sumx = tr[da].tagx * tr[da << 1].siz + getsum1(tr[da << 1].l, tr[da << 1].r);
tr[da << 1 | 1].sumx =
tr[da].tagx * tr[da << 1 | 1].siz + getsum1(tr[da << 1 | 1].l, tr[da << 1 | 1].r);
tr[da << 1].sumy = tr[da].tagy * tr[da << 1].siz + getsum1(tr[da << 1].l, tr[da << 1].r);
tr[da << 1 | 1].sumy =
tr[da].tagy * tr[da << 1 | 1].siz + getsum1(tr[da << 1 | 1].l, tr[da << 1 | 1].r);
tr[da << 1].sumxx = tr[da << 1].siz * tr[da].tagx * tr[da].tagx +
2.0 * tr[da].tagx * getsum1(tr[da << 1].l, tr[da << 1].r) +
getsum2(tr[da << 1].r) - getsum2(tr[da << 1].l - 1);
tr[da << 1 | 1].sumxx = tr[da << 1 | 1].siz * tr[da].tagx * tr[da].tagx +
2.0 * tr[da].tagx * getsum1(tr[da << 1 | 1].l, tr[da << 1 | 1].r) +
getsum2(tr[da << 1 | 1].r) - getsum2(tr[da << 1 | 1].l - 1);
tr[da << 1].sumxy = tr[da << 1].siz * tr[da].tagx * tr[da].tagy +
(tr[da].tagx + tr[da].tagy) * getsum1(tr[da << 1].l, tr[da << 1].r) +
getsum2(tr[da << 1].r) - getsum2(tr[da << 1].l - 1);
tr[da << 1 | 1].sumxy = tr[da << 1 | 1].siz * tr[da].tagx * tr[da].tagy +
(tr[da].tagx + tr[da].tagy) * getsum1(tr[da << 1 | 1].l, tr[da << 1 | 1].r) +
getsum2(tr[da << 1 | 1].r) - getsum2(tr[da << 1 | 1].l - 1);
tr[da].tagx = tr[da].tagy = 1e18;
tr[da << 1].lazx = tr[da << 1 | 1].lazx = tr[da << 1].lazy = tr[da << 1 | 1].lazy = 0;
}
if (tr[da].lazx != 0 || tr[da].lazy != 0) {
tr[da << 1].lazx += tr[da].lazx;
tr[da << 1 | 1].lazx += tr[da].lazx;
tr[da << 1].lazy += tr[da].lazy;
tr[da << 1 | 1].lazy += tr[da].lazy;
tr[da << 1].sumxx +=
2.0 * tr[da].lazx * tr[da << 1].sumx + tr[da << 1].siz * tr[da].lazx * tr[da].lazx;
tr[da << 1 | 1].sumxx +=
2.0 * tr[da].lazx * tr[da << 1 | 1].sumx + tr[da << 1 | 1].siz * tr[da].lazx * tr[da].lazx;
tr[da << 1].sumxy += tr[da << 1].sumx * tr[da].lazy + tr[da << 1].sumy * tr[da].lazx +
tr[da << 1].siz * tr[da].lazx * tr[da].lazy;
tr[da << 1 | 1].sumxy += tr[da << 1 | 1].sumx * tr[da].lazy + tr[da << 1 | 1].sumy * tr[da].lazx +
tr[da << 1 | 1].siz * tr[da].lazx * tr[da].lazy;
tr[da << 1].sumx += tr[da << 1].siz * tr[da].lazx;
tr[da << 1 | 1].sumx += tr[da << 1 | 1].siz * tr[da].lazx;
tr[da << 1].sumy += tr[da << 1].siz * tr[da].lazy;
tr[da << 1 | 1].sumy += tr[da << 1 | 1].siz * tr[da].lazy;
tr[da].lazx = tr[da].lazy = 0;
}
}
void build(int da, int l, int r) {
tr[da].l = l, tr[da].r = r, tr[da].siz = r - l + 1;
if (tr[da].l == tr[da].r) {
tr[da].sumx = jlx[l];
tr[da].sumy = jly[l];
tr[da].sumxx = jlx[l] * jlx[l];
tr[da].sumxy = jlx[l] * jly[l];
return;
}
rg int mids = (tr[da].l + tr[da].r) >> 1;
build(da << 1, l, mids);
build(da << 1 | 1, mids + 1, r);
push_up(da);
}
void ad(int da, int l, int r, db valx, db valy) {
if (tr[da].l >= l && tr[da].r <= r) {
tr[da].lazx += valx;
tr[da].lazy += valy;
tr[da].sumxx += 2.0 * valx * tr[da].sumx + tr[da].siz * valx * valx;
tr[da].sumxy += tr[da].sumx * valy + tr[da].sumy * valx + tr[da].siz * valx * valy;
tr[da].sumx += tr[da].siz * valx;
tr[da].sumy += tr[da].siz * valy;
return;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
if (l <= mids)
ad(da << 1, l, r, valx, valy);
if (r > mids)
ad(da << 1 | 1, l, r, valx, valy);
push_up(da);
}
void xg(int da, int l, int r, db valx, db valy) {
if (tr[da].l >= l && tr[da].r <= r) {
tr[da].lazx = 0, tr[da].lazy = 0;
tr[da].tagx = valx;
tr[da].tagy = valy;
tr[da].sumx = valx * tr[da].siz + getsum1(tr[da].l, tr[da].r);
tr[da].sumy = valy * tr[da].siz + getsum1(tr[da].l, tr[da].r);
tr[da].sumxx = tr[da].siz * valx * valx + 2.0 * valx * getsum1(tr[da].l, tr[da].r) +
getsum2(tr[da].r) - getsum2(tr[da].l - 1);
tr[da].sumxy = tr[da].siz * valx * valy + (valx + valy) * getsum1(tr[da].l, tr[da].r) +
getsum2(tr[da].r) - getsum2(tr[da].l - 1);
return;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
if (l <= mids)
xg(da << 1, l, r, valx, valy);
if (r > mids)
xg(da << 1 | 1, l, r, valx, valy);
push_up(da);
}
db cxx(int da, int l, int r) {
if (tr[da].l >= l && tr[da].r <= r) {
return tr[da].sumx;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
rg db nans = 0;
if (l <= mids)
nans += cxx(da << 1, l, r);
if (r > mids)
nans += cxx(da << 1 | 1, l, r);
return nans;
}
db cxy(int da, int l, int r) {
if (tr[da].l >= l && tr[da].r <= r) {
return tr[da].sumy;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
rg db nans = 0;
if (l <= mids)
nans += cxy(da << 1, l, r);
if (r > mids)
nans += cxy(da << 1 | 1, l, r);
return nans;
}
db cxxx(int da, int l, int r) {
if (tr[da].l >= l && tr[da].r <= r) {
return tr[da].sumxx;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
rg db nans = 0;
if (l <= mids)
nans += cxxx(da << 1, l, r);
if (r > mids)
nans += cxxx(da << 1 | 1, l, r);
return nans;
}
db cxxy(int da, int l, int r) {
if (tr[da].l >= l && tr[da].r <= r) {
return tr[da].sumxy;
}
push_down(da);
rg int mids = (tr[da].l + tr[da].r) >> 1;
rg db nans = 0;
if (l <= mids)
nans += cxxy(da << 1, l, r);
if (r > mids)
nans += cxxy(da << 1 | 1, l, r);
return nans;
}
db getx(int l, int r) { return (db)cxx(1, l, r) / (r - l + 1); }
db gety(int l, int r) { return (db)cxy(1, l, r) / (r - l + 1); }
void solve(int l, int r) {
db ans1 = cxxy(1, l, r) - (db)(r - l + 1) * getx(l, r) * gety(l, r);
db ans2 = cxxx(1, l, r) - (db)(r - l + 1) * getx(l, r) * getx(l, r);
printf("%.10f\n", ans1 / ans2);
}
int main() {
scanf("%d%d", &n, &m);
for (rg int i = 1; i <= n; i++) {
scanf("%lf", &jlx[i]);
}
for (rg int i = 1; i <= n; i++) {
scanf("%lf", &jly[i]);
}
build(1, 1, n);
rg int aa, bb, cc;
db dd, ee;
for (rg int i = 1; i <= m; i++) {
scanf("%d%d%d", &aa, &bb, &cc);
if (aa == 1) {
solve(bb, cc);
} else if (aa == 2) {
scanf("%lf%lf", &dd, &ee);
ad(1, bb, cc, dd, ee);
} else {
scanf("%lf%lf", &dd, &ee);
xg(1, bb, cc, dd, ee);
}
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· 大模型工具KTransformer的安装
· [计算机/硬件/GPU] 显卡