相关分析[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} sumlimits _{i=L} ^R x_i)

(overline{y}={1 over R-L+1} sumlimits _{i=L} ^R y_i)

如果直线方程是(y=ax+b),那么(a)应当这样计算:

(a={sumlimits_{i=L} ^R (x_i-overline{x})(y_i-overline{y}) over sumlimits _{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={sumlimits_{i=L} ^R (x_i-overline{x})(y_i-overline{y}) over sumlimits _{i=L} ^R (x_i -overline{x})^2})

(={sumlimits_{i=L} ^R (x_i*y_i-overline{x}*y_i-x_i*overline{y}+overline{x}*overline{y}) over sumlimits _{i=L} ^R (x_i^2 -2*overline{x}*x_i+overline{x}^2)})

操作3可以看作先将(x_i,y_iin [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
", 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
很模板的一个东西 查询的函数也差不多 但是最后不需要更新当前节点信息了

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM56.html