【题目描述】
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清零

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#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$

1
2
3
4
5
6
7
8
9
if (l == r) {
//进行修改
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (pos <= mid) //向左儿子递归
else //向右儿子递归
//在最后要更新当前节点信息

区间修改$[x,y]$

1
2
3
4
5
6
7
8
9
if (x <= l && r <= y) {
//修改+打标记
return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (x <= mid) //向左儿子递归
if (mid < y) //向右儿子递归
//在最后要更新当前节点信息

pushdown记得清空父节点的tag
很模板的一个东西 查询的函数也差不多 但是最后不需要更新当前节点信息了

评论