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

  • 相关阅读:
    OpenGL纹理数据块
    MFC应用真彩色图标资源
    PictureConverter命令行图片批量转换工具
    Google Earth6.1 tree
    OpenGL Render On Window Process
    纹理滤波(Texture Filter)
    使用开源OpenCTM进行常用三维格式互导
    《搅基辞》
    访问WebDAV服务
    linux 挂载一个文件夹到另一个文件夹
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM56.html
Copyright © 2011-2022 走看看