Description

你需要维护一个长度为 \(n\) 的实数对的序列,第 \(i\) 个元素为 \((x_i, y_i)\)。现有 \(m\) 次操作:

  • \(\texttt{1 L R}\):设区间 \([L, R]\) 的平均数 \(\bar x = \frac{\sum_{i=L}^R x_i}{R-L+1},\bar y = \frac{\sum_{i=L}^R y_i}{R-L+1}\),求
\[a = \dfrac{\sum_{i=L}^R (x_i - \bar x)(y_i - \bar y)}{\sum_{i=L}^R (x_i - \bar x)^2}
\]

的值。误差不超过 \(10^{-5}\)。

  • \(\texttt{2 L R S T}\):进行如下修改:

    \(\forall i\in[L, R],\quad x_i \leftarrow x_i + S\)

    \(\forall i\in[L, R],\quad y_i \leftarrow y_i + T\)

  • \(\texttt{3 L R S T}\):进行如下修改:

    \(\forall i \in [L, R],\quad x_i \leftarrow i + S\)

    \(\forall i \in [L, R],\quad y_i \leftarrow i + T\)

Hint

\(1\le n, m\le 10^5, 0\le |S|, |T|\le 10^5, 0\le |x_i|, |y_i|\le 10^5\)

Solution

一道细节很多的线段树练手题。

在考虑怎么支持这些操作时不妨先看看我们需要维护些什么东西。拆开 \(a\) 这个柿子:

(下面用 \(\sum\) 代替 \(\sum_{i=L}^R\),用 \(n\) 代替 \(R - L + 1\))

\[\begin{aligned}
a = & \dfrac{\sum (x_i - \bar x)(y_i - \bar y)}{\sum (x_i - \bar x)^2} = \dfrac A B \\ \\
A = & \sum (x_i y_i - x_i \bar y - y_i \bar x_i + \bar x \bar y) \\
= & \sum x_i y_i - \bar y\sum x_i - \bar x\sum y_i + n\bar x \bar y \\
= & \sum x_i y_i - \tfrac{1}{n}\sum x_i \sum y_i \\ \\
B = & \sum (x_i^2 + {\bar x}^2 - 2x_i\bar x) \\
= & \sum x_i^2 +n{\bar x}^2 - 2\bar x\sum x_i \\
= & \sum x_i^2 -\tfrac{1}{n}\left(\sum x_i\right)^2\\
\end{aligned}
\]

将 \(\bar x, \bar y\) 展开的话,可以发现我们需要维护四个东西:

\[\begin{aligned}
A = & \boxed{\sum x_i y_i} - \tfrac{1}{n}\boxed{\sum x_i}\times \boxed{\sum y_i} \\
B = & \boxed{\sum x_i^2} -\tfrac{1}{n}\left(\sum x_i\right)^2\\
\end{aligned}
\]

整坨柿子瞬间简洁可做了。


那么如何维护这四个信息呢?为了方便,我们用 \(v_1, v_2, v_3, v_4\) 分别代表 \(\sum x_i, \sum y_i,\sum x_i^2,\sum x_i y_i\)。

考虑修改操作之后这四个信息的变化:

  • 操作 \(2\):
\[\begin{aligned}
&\sum x_i\to\sum(x_i+S)=\sum x_i+nS & v_1\to v_1 + nS \\
&\sum y_i\to\sum(y_i+T)=\sum y_i+nT & v_2\to v_2 + nT \\
&\sum x_i^2\to\sum(x_i+S)^2=\sum x_i^2+nS^2+2S\sum x_i& v_3\to v_3+nS^2+2Sv_1\\
&\sum x_i y_i \to\sum(x_i+S)(y_i+T)=\sum x_i y_i+T\sum x_i+S\sum y_i+nST & v_4 \to v_4+Tv_1+Sv_2+nST\\
\end{aligned}
\]
  • 操作 \(3\):
  • 记 \(s_1 = \sum_{i=L}^R i, s_2 = \sum_{i=L}^R i^2\)。
\[\begin{aligned}
&\sum x_i\to\sum(i+S)=s_1+nS & v_1\to s_1 + nS \\
&\sum y_i\to\sum(i+T)=s_1+nT & v_2\to s_1 + nT \\
&\sum x_i^2\to\sum(i+S)^2=s_2+nS^2+2Ss_1& v_3\to s_2+nS^2+2Ss_1\\
&\sum x_i y_i \to\sum(i+S)(i+T)=s_2+(T+S)s_1+nST & v_4 \to s_2+(T+S)s_1+nST\\
\end{aligned}
\]

于是就这样维护就行了,注意操作二的更新顺序。

其中 \(s_1, s_2\) 的计算可以预处理,也可以直接使用公式:

  • \(\sum_{j=1}^i j = \frac{i(i+1)}{2}\)
  • \(\sum_{j=1}^i j^2 = \frac{i(i+1)(2i+1)}{6}\)

Code

/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : SDOI2017 相关分析
*/
#include <algorithm>
#include <cmath>
#include <cstdio> using namespace std;
const int N = 1e5 + 5;
const int S = N << 2; namespace calculator {
inline double sum(const double& l, const double& r) {
return (l + r) * (r - l + 1) / 2;
}
inline double sqrs(const double& p) {
return p * (p + 1) * (2 * p + 1) / 6;
}
} struct dataType {
double sumx, sumy, sqrs, muls;
inline dataType()
: sumx(0.0), sumy(0.0), sqrs(0.0), muls(0.0) { }
inline dataType(const double& a, const double& b,
const double& c, const double& d)
: sumx(a), sumy(b), sqrs(c), muls(d) { }
inline dataType(const double& x, const double& y)
: sumx(x), sumy(y), sqrs(x * x), muls(x * y) { }
inline dataType operator + (const dataType& rhs) const {
return dataType(this->sumx + rhs.sumx, this->sumy + rhs.sumy,
this->sqrs + rhs.sqrs, this->muls + rhs.muls);
}
inline void update(const dataType& rhs) {
this->sumx += rhs.sumx, this->sumy += rhs.sumy;
this->sqrs += rhs.sqrs, this->muls += rhs.muls;
}
}; struct tagType {
double S, T;
inline tagType()
: S(0.0), T(0.0) { }
inline tagType(const double& s, const double& t)
: S(s), T(t) { }
inline bool operator == (const tagType& rhs) const {
return (fabs(this->S - rhs.S) <= 1e-8) && (fabs(this->T - rhs.T) <= 1e-8);
}
inline bool operator != (const tagType& rhs) const {
return !(*this == rhs);
}
};
const tagType std_cov(-1e18, -1e18);
const tagType std_add(0, 0); int L[S], R[S];
dataType tr[S];
tagType cov[S];
tagType add[S]; #define mid ((L[x] + R[x]) / 2)
#define len double(R[x] - L[x] + 1) inline void pushup(int x) {
tr[x] = tr[x << 1] + tr[x << 1 | 1];
}
inline void setCov(int x, const tagType& v) {
double s1 = calculator::sum(L[x], R[x]);
double s2 = calculator::sqrs(R[x]) - calculator::sqrs(L[x] - 1); tr[x].sqrs = s2 + len * v.S * v.S + 2 * v.S * s1;
tr[x].muls = s2 + (v.S + v.T) * s1 + len * v.S * v.T;
tr[x].sumx = s1 + len * v.S;
tr[x].sumy = s1 + len * v.T;
cov[x] = v, add[x] = std_add;
}
inline void setAdd(int x, const tagType& v) {
tr[x].sqrs += v.S * v.S * len + 2 * v.S * tr[x].sumx;
tr[x].muls += v.S * tr[x].sumy + v.T * tr[x].sumx + len * v.S * v.T;
tr[x].sumx += v.S * len, tr[x].sumy += v.T * len;
add[x].S += v.S, add[x].T += v.T;
} inline void pushdown(int x) {
if (cov[x] != std_cov) {
setCov(x << 1, cov[x]);
setCov(x << 1 | 1, cov[x]);
cov[x] = std_cov;
}
if (add[x] != std_add) {
setAdd(x << 1, add[x]);
setAdd(x << 1 | 1, add[x]);
add[x] = std_add;
}
} void build(int x, int l, int r, double* datx, double* daty) {
L[x] = l, R[x] = r;
cov[x] = std_cov, add[x] = std_add;
if (l == r) {
tr[x] = dataType(datx[l], daty[l]);
return;
}
build(x << 1, l, mid, datx, daty);
build(x << 1 | 1, mid + 1, r, datx, daty);
pushup(x);
}
void modify(int x, int l, int r, void(*update)(int, const tagType&), const tagType& v) {
if (l <= L[x] && R[x] <= r) return update(x, v);
pushdown(x);
if (l <= mid) modify(x << 1, l, r, update, v);
if (r > mid) modify(x << 1 | 1, l, r, update, v);
pushup(x);
}
void query(int x, int l, int r, dataType& ret) {
if (l <= L[x] && R[x] <= r) return ret.update(tr[x]);
if (l > R[x] || L[x] > r) return;
pushdown(x), query(x << 1, l, r, ret), query(x << 1 | 1, l, r, ret);
} #undef mid
#undef len int n, Q;
double x[N], y[N]; signed main() {
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; i++) scanf("%lf", x + i);
for (int i = 1; i <= n; i++) scanf("%lf", y + i); build(1, 1, n, x, y); while (Q--) {
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r); if (opt == 1) {
dataType res; query(1, l, r, res);
double len = r - l + 1;
double avex = res.sumx / len;
double avey = res.sumy / len; double A = res.muls - res.sumx * res.sumy / len;
double B = res.sqrs - res.sumx * res.sumx / len; printf("%lf\n", A / B);
} else {
tagType val; scanf("%lf%lf", &val.S, &val.T);
modify(1, l, r, (opt == 2 ? setAdd : setCov), val);
}
}
}

【SDOI2017】相关分析(线段树)的更多相关文章

  1. [Sdoi2017]相关分析 [线段树]

    [Sdoi2017]相关分析 题意:沙茶线段树 md其实我考场上还剩一个多小时写了40分 其实当时写正解也可以吧1h也就写完了不过还要拍一下 正解代码比40分短2333 #include <io ...

  2. 【BZOJ4821】[Sdoi2017]相关分析 线段树

    [BZOJ4821][Sdoi2017]相关分析 Description Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. ...

  3. BZOJ 4821 [Sdoi2017]相关分析 ——线段树

    打开题面,看到许多$\sum$ woc,好神啊,SDOI好强啊 然后展开之后,woc,SDOI好弱啊,怎么T3出个线段树裸题啊. 最后写代码的时候,woc,SDOI怎么出个这么码农的题啊,怎么调啊. ...

  4. 洛谷P3707 [SDOI2017]相关分析(线段树)

    题目描述 Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. Frank不仅喜欢观测,还喜欢分析观测到的数据.他经常分析两个 ...

  5. BZOJ 4821: [Sdoi2017]相关分析 线段树 + 卡精

    考试的时候切掉了,然而卡精 + 有一个地方忘开 $long long$,完美挂掉 $50$pts. 把式子化简一下,然后直接拿线段树来维护即可. Code: // luogu-judger-enabl ...

  6. BZOJ.4821.[SDOI2017]相关分析(线段树)

    BZOJ LOJ 洛谷 恶心的拆式子..然后就是要维护\(\sum x_i,\ \sum y_i,\ \sum x_iy_i,\ \sum x_i^2\). 操作三可以看成初始化一遍,然后同操作二. ...

  7. SDOI2017相关分析 线段树

    题目 https://loj.ac/problem/2005 思路 \[ \sum_{L}^{R}{(x_i-x)^{2}} \] \[ \sum_{L}^{R}{(x_i^2-2*x_i*x+x^{ ...

  8. 【BZOJ4821】【SDOI2017】相关分析 [线段树]

    相关分析 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Frank对天文学非常感兴趣,他经 ...

  9. luogu3707 相关分析 (线段树)

    把式子展开以后会发现,可以用线段树维护$x,y,x*y,x^2$分别的区间和 然后操作有区间加和区间修改 这个pushdown的时候,如果改和加的标记同时存在,那一定是先改再加,要不然加的标记已经被清 ...

  10. LOJ #2005. 「SDOI2017」相关分析 线段树维护回归直线方程

    题目描述 \(Frank\) 对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. \(Frank\) 不仅喜欢观测,还喜欢分析观测到的 ...

随机推荐

  1. 45. 跳跃游戏 II

    给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 示例: 输入: [2,3,1,1,4]输出 ...

  2. linux全局和个人配置文件说明

    1.bash配置文件: 1).全局(bash的配置文件) 有 /etc/profile   /etc/profile.d/*  与 /etc/bashrc 其实都是bash这个程序启动的时候会读取配置 ...

  3. 利用Vmware workstation安装MS-DOS使用Turbo C 2.0

    首先上各种软件的链接 链接:https://pan.baidu.com/s/1HfTDexiJSpioUnzKddUIyA 提取码:khtu 这个链接是压缩后的 链接:https://pan.baid ...

  4. Java之定时任务全家桶

    定时任务应用非常广泛,Java提供的现有解决方案有很多.本次主要讲schedule.quartz.xxl-job.shedlock等相关的代码实践. 一.SpringBoot使用Schedule 核心 ...

  5. 【转】AWVS扫描小技巧

    1.文件头加 Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html) 可以绕过狗,waf ...

  6. PHP 使用gd库给图片加图片水印,文字水印,并返回路径

    //图片水印 $mark_filename = './02.jpg'; //获取图片信息 $info = getimagesize($mark_filename); //获取图片的后缀 $type = ...

  7. scala中的val,var和lazy

    转自:https://yerias.github.io/2020/03/19/scala/3/#3%EF%BC%9Alazy%E4%BF%AE%E9%A5%B0%E7%AC%A6%E5%8F%AF%E ...

  8. Java的比较器Comparable与Comparator

    在Java中有两个比较器:Comparable.Comparator 对于Integer.Double等等类型,可以直接对他们进行比较,因为已经实现了比较的方式,然而在平时常常会面临需要对集合进行排序 ...

  9. IDEA主题加高亮

    IntelliJ Idea的黑色主题,使用就是file-->import settings 选择压缩包里的jar包,主题就被导入了,之后会提示重启,重启完就可以在设置中使用了. IDEA主题下载 ...

  10. 2021版的思维导图MindManager 安装激活以及换机教程

    思维导图软件MindManager更新版本啦,相信细心的小伙伴应该已经发现了,MindManager最新版的名称变成了MindManager Windows 21,并且更新了很多新功能,点击查看Min ...