相关分析[SDOI2017]
【题目描述】
Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度、颜色等等,进而估算出星星的距离,半径等等。
Frank不仅喜欢观测,还喜欢分析观测到的数据。他经常分析两个参数之间(比如亮度和半径)是否存在某种关系。
现在Frank要分析参数\(X\)与\(Y\)之间的关系。他有\(n\)组观测数据,第\(i\)组观测数据记录了\(x_i,y_i\)。他需要以下几种操作:
1 L R
用直线拟合第\(L\)组到底\(R\)组观测数据。用\(\overline{x}\)表示这些观测数据中\(x\)的平均数,用\(\overline{y}\)表示这些观测数据中$y4的平均数,即
\(\overline{x}={1 \over R-L+1} \sum\limits _{i=L} ^R x_i\)
\(\overline{y}={1 \over R-L+1} \sum\limits _{i=L} ^R y_i\)
如果直线方程是\(y=ax+b\),那么\(a\)应当这样计算:
\(a={\sum\limits_{i=L} ^R (x_i-\overline{x})(y_i-\overline{y}) \over \sum\limits _{i=L} ^R (x_i -\overline{x})^2}\)
你需要帮助Frank计算\(a\)。
2 L R S T
Frank发现测量数据第\(L\)组到底\(R\)组数据有误差,对每个\(i\)满足\(L \leq i \leq R\),\(x_i\)需要加上\(S\),\(y_i\)需要加上\(T\)。
3 L R S T
Frank发现第\(L\)组到第\(R\)组数据需要修改,对于每个\(i\)满足\(L \leq i \leq R\),\(x_i\)需要修改为\((S+i)\),\(y_i\)需要修改为\((T+i)\)。
【输入格式】
第一行两个数\(n,m\),表示观测数据组数和操作次数。
接下来一行\(n\)个数,第\(i\)个数是\(x_i\)。
接下来一行\(n\)个数,第\(i\)个数是\(y_i\)。
接下来\(m\)行,表示操作,格式见题目描述。
【输出格式】
对于每个1操作,输出一行,表示直线斜率\(a\)。选手输出与标准输出的绝对误差或相对误差不超过\(10^{-5}\) 即为正确。
题解
\(a={\sum\limits_{i=L} ^R (x_i-\overline{x})(y_i-\overline{y}) \over \sum\limits _{i=L} ^R (x_i -\overline{x})^2}\)
\(={\sum\limits_{i=L} ^R (x_i*y_i-\overline{x}*y_i-x_i*\overline{y}+\overline{x}*\overline{y}) \over \sum\limits _{i=L} ^R (x_i^2 -2*\overline{x}*x_i+\overline{x}^2)}\)
操作3可以看作先将\(x_i,y_i\in [l,r]\)全部置为\(i\) 然后再进行一次2操作 显然是等价的
用线段树维护4个值:\(\sum x, \sum y, \sum x*y, \sum x^2\)
细节:
这里用\(sumx\)表示\(\sum x\),\(sumy\)表示\(\sum y\),\(xy\)表示\(\sum x*y\),\(xx\)表示\(\sum x^2\)。
操作2
将每个\(x_i\)加上\(S\),每个\(y_i\)加上\(T\)
\(sumx = sumx + (r-l+1) * S\)
\(sumy = sumy + (r-l+1) * T\)
\(xy = xy + (r-l+1)*S*T + sumx * T + sumy * S\)
\(xx = xx + (r-l+1)*S*S+2*sumx*S\)
对于操作2 懒标记就正常地打 上面的4个式子都是可以通过暴力拆括号得到的 不推了
操作3
将每个\(x_i,y_i\)都置为\(i\)
\(sumx = sumy = (r-l+1)*(l+1)/2\) 就是从\(l\)加到\(r\)。。。
\(xx = xy = l^2+(l+1)^2+(l+2)^2+\dots +r^2\) 这个可以用平方和公式\(O(1)\)算 平方和公式是\(1^2+2^2+\dots +n^2=\frac{n(n+1)(2n+1)}{6}\)
注意 打操作3的tag时 因为这个是直接修改类的操作 要把操作2的tag清零
代码
#include <bits/stdc++.h>
using namespace std;
typedef long double db;
int n, m;
db a[100005], b[100005];
int tp, l, r;
db s, t;
namespace Segtree{
struct tree{
int l, r;
db sumx, sumy, xy, xx;
int tagi;
db tagx, tagy;
} tr[400005];
#define lson ind<<1
#define rson ind<<1|1
inline void pushup(int ind) {
tr[ind].sumx = tr[lson].sumx + tr[rson].sumx;
tr[ind].sumy = tr[lson].sumy + tr[rson].sumy;
tr[ind].xx = tr[lson].xx + tr[rson].xx;
tr[ind].xy = tr[lson].xy + tr[rson].xy;
}
inline db getsum(int la, int ra) {
db l = la, r = ra;
return (r - l + 1) * (l + r) / 2;
}
inline db getsqr(int la, int ra) {
db l = la, r = ra;
return (r * (r + 1) * (2 * r + 1) - (l-1) * l * (2 * (l-1) + 1)) / 6;
}
inline void addxy(int ind, db vx, db vy) {
db len = (tr[ind].r - tr[ind].l + 1);
tr[ind].tagx += vx; tr[ind].tagy += vy;
tr[ind].xx += 2 * tr[ind].sumx * vx + vx * vx * len;
tr[ind].xy += len * vx * vy + tr[ind].sumx * vy + tr[ind].sumy * vx;
tr[ind].sumx += vx * len;
tr[ind].sumy += vy * len;
}
inline void addi(int ind) {
tr[ind].tagx = tr[ind].tagy = 0;
tr[ind].tagi = 1;
tr[ind].sumx = tr[ind].sumy = getsum(tr[ind].l, tr[ind].r);
tr[ind].xx = tr[ind].xy = getsqr(tr[ind].l, tr[ind].r);
}
inline void pushdown(int ind) {
if (tr[ind].tagi) {
tr[ind].tagi = 0;
addi(lson);
addi(rson);
}
if (tr[ind].tagx || tr[ind].tagy) {
int vx = tr[ind].tagx, vy = tr[ind].tagy;
tr[ind].tagx = tr[ind].tagy = 0;
addxy(lson, vx, vy);
addxy(rson, vx, vy);
}
}
void build(int ind, int l, int r) {
tr[ind].l = l, tr[ind].r = r;
tr[ind].tagi = tr[ind].tagx = tr[ind].tagy = 0;
if (l == r) {
tr[ind].sumx = a[l]; tr[ind].sumy = b[l];
tr[ind].xy = a[l] * b[l]; tr[ind].xx = a[l] * a[l];
return;
}
int mid = (l + r) >> 1;
build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
pushup(ind);
}
void update2(int ind, int x, int y, db vx, db vy) {
int l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
addxy(ind, vx, vy);
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (x <= mid) update2(lson, x, y, vx, vy);
if (mid < y) update2(rson, x, y, vx, vy);
pushup(ind);
}
void update3(int ind, int x, int y) {
int l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
addi(ind);
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (x <= mid) update3(lson, x, y);
if (mid < y) update3(rson, x, y);
pushup(ind);
}
inline tree merge(tree a, tree b) {
a.sumx += b.sumx;
a.sumy += b.sumy;
a.xx += b.xx;
a.xy += b.xy;
return a;
}
tree query(int ind, int x, int y) {
int l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
return tr[ind];
}
int mid = (l + r) >> 1;
pushdown(ind);
tree a, b;
a.sumx = a.sumy = a.xx = a.xy = 0;
b.sumx = b.sumy = b.xx = b.xy = 0;
if (x <= mid) a = query(lson, x, y);
if (mid < y) b = query(rson, x, y);
return merge(a, b);
}
}
using namespace Segtree;
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%Lf", &a[i]);
for (int i = 1; i <= n; i++) scanf("%Lf", &b[i]);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
scanf("%d", &tp);
if (tp == 1) {
scanf("%d %d", &l, &r);
tree ans = query(1, l, r);
db sumx = ans.sumx * 1.0, sumy = ans.sumy * 1.0, xx = ans.xx * 1.0, xy = ans.xy * 1.0;
db ax = sumx / (r - l + 1), ay = sumy / (r - l + 1);
db up = ax * ay * (r - l + 1) + xy - ay * sumx - ax * sumy;
db down = xx * 1.0 - 2.0 * ax * sumx + (r - l + 1) * ax * ax;
printf("%.10Lf\n", up / down);
} else if (tp == 2) {
scanf("%d %d %Lf %Lf", &l, &r, &s, &t);
update2(1, l, r, s, t);
} else {
scanf("%d %d %Lf %Lf", &l, &r, &s, &t);
update3(1, l, r);
update2(1, l, r, s, t);
}
}
return 0;
}
如何一遍写对线段树?
单点修改\(pos\)
if (l == r) {
//进行修改
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (pos <= mid) //向左儿子递归
else //向右儿子递归
//在最后要更新当前节点信息
区间修改\([x,y]\)
if (x <= l && r <= y) {
//修改+打标记
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (x <= mid) //向左儿子递归
if (mid < y) //向右儿子递归
//在最后要更新当前节点信息
pushdown
记得清空父节点的tag
很模板的一个东西 查询的函数也差不多 但是最后不需要更新当前节点信息了