相关分析[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
很模板的一个东西 查询的函数也差不多 但是最后不需要更新当前节点信息了

posted @ 2020-03-15 11:41  AK_DREAM  阅读(167)  评论(0编辑  收藏  举报