【SDOI2017】相关分析(线段树)
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}\),求
\]
的值。误差不超过 \(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\))
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\) 展开的话,可以发现我们需要维护四个东西:
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\):
&\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\)。
&\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】相关分析(线段树)的更多相关文章
- [Sdoi2017]相关分析 [线段树]
[Sdoi2017]相关分析 题意:沙茶线段树 md其实我考场上还剩一个多小时写了40分 其实当时写正解也可以吧1h也就写完了不过还要拍一下 正解代码比40分短2333 #include <io ...
- 【BZOJ4821】[Sdoi2017]相关分析 线段树
[BZOJ4821][Sdoi2017]相关分析 Description Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. ...
- BZOJ 4821 [Sdoi2017]相关分析 ——线段树
打开题面,看到许多$\sum$ woc,好神啊,SDOI好强啊 然后展开之后,woc,SDOI好弱啊,怎么T3出个线段树裸题啊. 最后写代码的时候,woc,SDOI怎么出个这么码农的题啊,怎么调啊. ...
- 洛谷P3707 [SDOI2017]相关分析(线段树)
题目描述 Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. Frank不仅喜欢观测,还喜欢分析观测到的数据.他经常分析两个 ...
- BZOJ 4821: [Sdoi2017]相关分析 线段树 + 卡精
考试的时候切掉了,然而卡精 + 有一个地方忘开 $long long$,完美挂掉 $50$pts. 把式子化简一下,然后直接拿线段树来维护即可. Code: // luogu-judger-enabl ...
- BZOJ.4821.[SDOI2017]相关分析(线段树)
BZOJ LOJ 洛谷 恶心的拆式子..然后就是要维护\(\sum x_i,\ \sum y_i,\ \sum x_iy_i,\ \sum x_i^2\). 操作三可以看成初始化一遍,然后同操作二. ...
- SDOI2017相关分析 线段树
题目 https://loj.ac/problem/2005 思路 \[ \sum_{L}^{R}{(x_i-x)^{2}} \] \[ \sum_{L}^{R}{(x_i^2-2*x_i*x+x^{ ...
- 【BZOJ4821】【SDOI2017】相关分析 [线段树]
相关分析 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description Frank对天文学非常感兴趣,他经 ...
- luogu3707 相关分析 (线段树)
把式子展开以后会发现,可以用线段树维护$x,y,x*y,x^2$分别的区间和 然后操作有区间加和区间修改 这个pushdown的时候,如果改和加的标记同时存在,那一定是先改再加,要不然加的标记已经被清 ...
- LOJ #2005. 「SDOI2017」相关分析 线段树维护回归直线方程
题目描述 \(Frank\) 对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. \(Frank\) 不仅喜欢观测,还喜欢分析观测到的 ...
随机推荐
- python dvwa布尔盲注自动化脚本(level=low)
仅供学习代码参考 1#python dvwa布尔盲注自动化脚本 2 import requests 3 import string 4 import time 5 INIT_URL="htt ...
- 关闭防火墙和设置主机名和ip及克隆机网卡处理方法
关闭防火墙: service NetworkManager stop --图形化用ifconfig之前先关掉网络服务. chkconfig NetworkManager off (将开机自启动关掉,使 ...
- 微软面试题: LeetCode 4. 寻找两个正序数组的中位数 hard 出现次数:3
题目描述: 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2.请你找出并返回这两个正序数组的中位数. 进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决 ...
- Java编发编程 - 线程池的认识(一)
每逢面试都会询问道线程池的概念和使用,但是工作中真正的又有多少场景使用呢?相信大家都会有这样的疑问:面试选拔造汽车,实际进公司就是拧螺丝!但是真正要把这颗螺丝拧紧,拧牢,没有这些最底层的知识做铺垫你可 ...
- PHP代码审计入门(SQL注入漏洞挖掘基础)
SQL注入漏洞 SQL注入经常出现在登陆页面.和获取HTTP头(user-agent/client-ip等).订单处理等地方,因为这几个地方是业务相对复杂的,登陆页面的注入现在来说大多数是发生在HTT ...
- vue中父子间传值和非父子间传值
vue传值一般分三种方式:父组件向子组件传值.子组件向父子间传值.非父子组件进行传值 一.父组件向子组件传值:父组件引用子组件后,通过数据绑定(v-bind)向子组件传值 父组件: <templ ...
- 博客新域名www.tecchen.tech
新年祝福 祝新的一年,大朋友实现所有梦想,小朋友健康成长- 新域名 https://www.tecchen.tech 有效期:10年 旧链接 之前的链接请自行替换为新链接地址,包括但不限于以下二级域名 ...
- guitar pro系列教程(二十二):Guitar Pro在乐谱上的工作【二】
我们在上一篇文章中给大家介绍了Guitar Pro的工作面板和音轨功能,今天我们将会给大家介绍Guitar Pro这款吉他谱学习软件得音频设置面板,在该面板中包含了声卡得选择.MIDI的输入输出及音轨 ...
- React Native两种加载图片的方式
1 加载网络图片 通过uri就可以加载网络图片 <Image source={{uri:'http://facebook.github.io/react/img/logo_og.png'}} s ...
- 实现 Application_Start 和 Application_End
理解 ASP.NET Core: 实现 Application_Start 和 Application_End 在 ASP.NET 中两个常用的处理节点是 Application_Start() 和 ...