P3707 & LOJ 2005 [SDOI2017] 相关分析 Solution

Description

给定序列 x = ( x 1 , x 2 , ⋯   , x n ) , y = ( y 1 , y 2 , ⋯   , y n ) x=(x_1,x_2,\cdots,x_n),y=(y_1,y_2,\cdots,y_n) x=(x1,x2,,xn),y=(y1,y2,,yn),有 m m m 个操作分三种:

  1. query ⁡ ( l , r ) \operatorname{query}(l,r) query(l,r):求 ∑ i = l r ( x i − x l ⋯ r ‾ ) ( y i − y l ⋯ r ‾ ) ∑ i = l r ( x i − x l ⋯ r ‾ ) 2 \dfrac{\sum_{i=l}^r (x_i-\overline{x_{l\cdots r}})(y_i-\overline{y_{l\cdots r}})}{\sum_{i=l}^r (x_i-\overline{x_{l\cdots r}})^2} i=lr(xixlr)2i=lr(xixlr)(yiylr)保证分母不为 0 0 0.
  2. add ⁡ ( l , r , s , t ) \operatorname{add}(l,r,s,t) add(l,r,s,t):对所有 i ∈ [ l , r ] i \in [l,r] i[l,r] 执行 x i ← x i + s ,    y i ← y i + t x_i \leftarrow \textcolor{red}{x_i} + s, \;y_i \leftarrow \textcolor{red}{y_i} + t xixi+s,yiyi+t.
  3. modify ⁡ ( l , r , s , t ) \operatorname{modify}(l,r,s,t) modify(l,r,s,t):对所有 i ∈ [ l , r ] i \in [l,r] i[l,r] 执行 x i ← i + s ,    y i ← i + t x_i \leftarrow \textcolor{red}{i} + s, \;y_i \leftarrow \textcolor{red}{i} + t xii+s,yii+t.

Limitations

1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1n,m105
∣ s ∣ , ∣ t ∣ ≤ 1 0 9 |s|,|t| \le 10^9 s,t109
0 ≤ ∣ x i ∣ , ∣ y i ∣ ≤ 1 0 5 0 \le |x_i|,|y_i| \le 10^5 0xi,yi105
1 s , 125 MB 1\text{s},125\text{MB} 1s,125MB

Solution

发现要求的式子难以维护,考虑化简,得到 ∑ x i y i − ∑ x i ∑ y i r − l + 1 ∑ x i 2 − ( ∑ x i ) 2 r − l + 1 \dfrac{\sum x_iy_i-\frac{\sum x_i \sum y_i}{r-l+1}}{\sum x_i^2 - \frac{(\sum x_i)^2}{r-l+1}} xi2rl+1(xi)2xiyirl+1xiyi(过程不好打所以省略)。
现在只需维护 ∑ x i ,    ∑ y i ,    ∑ x i 2 ,    ∑ x i y i \sum x_i,\;\sum y_i, \; \sum x_i^2,\; \sum x_iy_i xi,yi,xi2,xiyi,可以上线段树。
考虑 pushdown 如何写,发现后两个不好搞,同样化简式子:

  1. ∑ ( x i + s ) ( y i + t ) = ∑ x i y i + s ∑ y i + t ∑ x i + s t ( r − l + 1 ) \sum (x_i+s)(y_i+t)=\sum x_iy_i+s\sum y_i+t\sum x_i+st(r-l+1) (xi+s)(yi+t)=xiyi+syi+txi+st(rl+1)
  2. ∑ ( x i + s ) 2 = ∑ x i 2 + 2 s ∑ x i + s 2 ( r − l + 1 ) \sum (x_i+s)^2=\sum x^2_i+2s\sum x_i+s^2(r-l+1) (xi+s)2=xi2+2sxi+s2(rl+1)

然后写的时候注意顺序!!
还需要一个 x i ← i , y i ← i x_i \leftarrow i,y_i \leftarrow i xii,yii 的标记,写的时候需要用公式 1 2 + 2 2 + ⋯ + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 1^2+2^2+\cdots+n^2=\dfrac{n(n+1)(2n+1)}{6} 12+22++n2=6n(n+1)(2n+1)

剩下的不必多说,和普通的是一样的。
注意全部要开 double 因为可能爆 long long

Code

4.74 KB , 1.21 s , 14.11 MB    (in   total,   C++   20   with   O2) 4.74\text{KB},1.21\text{s},14.11\text{MB} \; \texttt{(in total, C++ 20 with O2)} 4.74KB,1.21s,14.11MB(in total, C++ 20 with O2)

// Problem: P3707 [SDOI2017] 相关分析
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3707
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

namespace seg_tree {
	struct Node {
	    int l, r;
	    f8 sum_x, sum_y, sum_xx, sum_xy, tag_x, tag_y;
	    bool cover;
	};
	
	inline int ls(int u) { return 2 * u + 1; }
	inline int rs(int u) { return 2 * u + 2; }
	inline f8 sqsum(f8 a) { return a * (a + 1) * (2 * a + 1) / 6; }
	
	struct SegTree {
		vector<Node> tr;
		inline SegTree() {}
		inline SegTree(const vector<f8>& x, const vector<f8>& y) {
			const int n = x.size();
			tr.resize(n << 1);
			build(0, 0, n - 1, x, y);
		}
		
		inline void merge(Node& res, const Node& le, const Node& ri) {
		    res.sum_x = le.sum_x + ri.sum_x;
		    res.sum_y = le.sum_y + ri.sum_y;
		    res.sum_xx = le.sum_xx + ri.sum_xx;
		    res.sum_xy = le.sum_xy + ri.sum_xy;
		}
		
		inline void pushup(int u, int mid) { merge(tr[u], tr[ls(mid)], tr[rs(mid)]); }
		
		void build(int u, int l, int r, const vector<f8>& x, const vector<f8>& y) {
		    tr[u].l = l, tr[u].r = r;
		    if (l == r) {
		        tr[u].sum_x = x[l];
		        tr[u].sum_y = y[l];
		        tr[u].sum_xx = x[l] * x[l];
		        tr[u].sum_xy = x[l] * y[l];
		        return;
		    }
		    const int mid = (l + r) >> 1;
		    build(ls(mid), l, mid, x, y);
		    build(rs(mid), mid + 1, r, x, y);
		    pushup(u, mid);
		}
		
		inline void fix(int u) {
		    const f8 lef = tr[u].l, rig = tr[u].r;
		    tr[u].tag_x = tr[u].tag_y = 0;
		    tr[u].cover = true;
		    tr[u].sum_x = tr[u].sum_y = (lef + rig + 2) * (rig - lef + 1) / 2;
		    tr[u].sum_xx = tr[u].sum_xy = sqsum(rig + 1) - sqsum(lef);
		}
		
		inline void apply(int u, f8 tag_x, f8 tag_y) {
		    const int len = tr[u].r - tr[u].l + 1;
		    tr[u].tag_x += tag_x;
		    tr[u].tag_y += tag_y;
		    
		    tr[u].sum_xy += tag_y * tr[u].sum_x + tag_x * tr[u].sum_y + tag_x * tag_y * len;
		    tr[u].sum_xx += 2 * tag_x * tr[u].sum_x + tag_x * tag_x * len;
		    tr[u].sum_x += tag_x * len;
		    tr[u].sum_y += tag_y * len;
		}
		
		void pushdown(int u, int mid) {
		    if (tr[u].cover) {
		        fix(ls(mid)), fix(rs(mid));
		        tr[u].cover = false;
		    }
		    apply(ls(mid), tr[u].tag_x, tr[u].tag_y);
		    apply(rs(mid), tr[u].tag_x, tr[u].tag_y);
		    tr[u].tag_x = tr[u].tag_y = 0;
		}
		
		void add(int u, int l, int r, f8 x, f8 y) {
		    if (l <= tr[u].l && tr[u].r <= r) return apply(u, x, y);
		    const int mid = (tr[u].l + tr[u].r) >> 1;
		    pushdown(u, mid);
		    if (l <= mid) add(ls(mid), l, r, x, y);
		    if (r > mid) add(rs(mid), l, r, x, y);
		    pushup(u, mid);
		}
		
		void update(int u, int l, int r, f8 x, f8 y) {
		    if (l <= tr[u].l && tr[u].r <= r) return (fix(u), apply(u, x, y));
		    const int mid = (tr[u].l + tr[u].r) >> 1;
		    pushdown(u, mid);
		    if (l <= mid) update(ls(mid), l, r, x, y);
		    if (r > mid) update(rs(mid), l, r, x, y);
		    pushup(u, mid);
		}
		
		Node query(int u, int l, int r) {
		    if (l <= tr[u].l && tr[u].r <= r) return tr[u];
		    const int mid = (tr[u].l + tr[u].r) >> 1;
		    pushdown(u, mid);
		    if (r <= mid) return query(ls(mid), l, r);
		    if (l > mid) return query(rs(mid), l, r);
		    Node res, le = query(ls(mid), l, r), ri = query(rs(mid), l, r);
		    return (merge(res, le, ri), res);
		}
		
		inline void range_add(int l, int r, f8 s, f8 t) { add(0, l, r, s, t); }
		inline void range_set(int l, int r, f8 s, f8 t) { update(0, l, r, s, t); }
		inline f8 range_slope(int l, int r) {
			Node res = query(0, l, r);
	    	const int len = r - l + 1;
		    f8 num = res.sum_xy - res.sum_x * res.sum_y / len;
		    f8 den = res.sum_xx - res.sum_x * res.sum_x / len;
		    return num / den;
		}
	};
}
using seg_tree::SegTree;

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m;
	scanf("%d %d", &n, &m);
	
	vector<f8> x(n), y(n);
	for (int i = 0; i < n; i++) scanf("%lf", &x[i]);
	for (int i = 0; i < n; i++) scanf("%lf", &y[i]);
	
	SegTree sgt(x, y);
	for (int i = 0, op, l, r; i < m; i++) {
	    scanf("%d %d %d", &op, &l, &r), l--, r--;
	    if (op == 1) printf("%.10lf\n", sgt.range_slope(l, r));
	    else if (op == 2) {
	        f8 s, t; scanf("%lf %lf", &s, &t);
	        sgt.range_add(l, r, s, t);
	    }
	    else {
	        f8 s, t; scanf("%lf %lf", &s, &t);
	        sgt.range_set(l, r, s, t);
	    }
	};
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值