LG P9844 [ICPC 2021 Nanjing R] Paimon Segment Tree Solution

Description

给定序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an),有 m m m 次修改 ( l , r , v ) (l,r,v) (l,r,v):

  • 对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r],令 a i ← a i + v a_i\gets a_i+v ai←ai+v.

执行完所有修改后,进行 q q q 次查询 ( l , r , L , R ) (l,r,L,R) (l,r,L,R),你需要求出 a l ∼ a r a_l\sim a_r al∼ar 在第 [ L , R ] [L,R] [L,R] 秒间的历史平方和,对 1 0 9 + 7 10^9+7 109+7 取模.

初始时为第 0 0 0 秒,第 i i i 次修改发生在第 i i i 秒.

Limitations

1 ≤ n , m , q ≤ 5 × 1 0 4 1\le n,m,q\le 5\times 10^4 1≤n,m,q≤5×104
1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1≤l≤r≤n
0 ≤ L ≤ R ≤ m 0\le L\le R\le m 0≤L≤R≤m
∣ a i ∣ , ∣ v ∣ < 1 0 9 + 7 |a_i|,|v|<10^9+7 ∣ai∣,∣v∣<109+7
4 s , 256 MB 4\text{s},256\text{MB} 4s,256MB

Solution

首先将询问拆成 [ 0 , y ] [0,y] [0,y] 和 [ 0 , x − 1 ] [0,x-1] [0,x−1],在时间轴上做扫描线.

然后需要一个 ds,支持区间加,区间历史平方和.

需要维护很多 tag \textit{tag} tag,考虑矩阵,维护区间长度 l e n len len,区间和 s 1 s_1 s1,区间平方和 s 2 s_2 s2,历史平方和 h h h.

左行右列的口诀,不难推导出转移矩阵:

  • 标记一个版本: [ l , s 1 , s 2 , h ] × [ 1 , 0 , 0 , 0 0 , 1 , 0 , 0 0 , 0 , 1 , 1 0 , 0 , 0 , 1 ] = [ l , s 1 , s 2 , h ′ ] \begin{bmatrix}l,s_1,s_2,h\end{bmatrix}\times\begin{bmatrix}1,0,0,0\\0,1,0,0\\0,0,1,1\\0,0,0,1\end{bmatrix}=\begin{bmatrix}l,s_1,s_2,h^\prime\end{bmatrix} [l,s1,s2,h]× 1,0,0,00,1,0,00,0,1,10,0,0,1 =[l,s1,s2,h′]
  • 区间加 v v v: [ l , s 1 , s 2 , h ] × [ 1 , v , v 2 , v 2 0 , 1 , 2 v , 2 v 0 , 0 , 1 , 0 0 , 0 , 0 , 1 ] = [ l , s 1 , s 2 , h ′ ] \begin{bmatrix}l,s_1,s_2,h\end{bmatrix}\times\begin{bmatrix}1,v,v^2,v^2\\0,1,2v,2v\\0,0,1,0\\0,0,0,1\end{bmatrix}=\begin{bmatrix}l,s_1,s_2,h^\prime\end{bmatrix} [l,s1,s2,h]× 1,v,v2,v20,1,2v,2v0,0,1,00,0,0,1 =[l,s1,s2,h′]

然后直接套 P7453 的矩阵线段树即可(甚至矩阵大小都一样).

常数方面,如果你 P7453 的代码能稳过,应该没什么问题.

时间复杂度 O ( k 3 ( m + q ) log ⁡ n ) O(k^3(m+q)\log n) O(k3(m+q)logn),其中 k = 4 k=4 k=4.

Code

5.54 KB , 0.99 s , 18.14 MB    (maximum,C++20 with O2) 5.54\text{KB},0.99\text{s},18.14\text{MB}\;\texttt{(maximum,C++20 with O2)} 5.54KB,0.99s,18.14MB(maximum,C++20 with O2)

cpp 复制代码
#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 fastio {}
using fastio::read;
using fastio::write;

constexpr int mod = 1e9 + 7;
inline int add(int x, int y) {return x + y >= mod ? x + y - mod : x + y; }

namespace matrix {}
using matrix::Mat;

namespace seg_tree {
	struct Node {
		int l, r;
		Mat val, tag;
	};
	
	inline int ls(int u) { return 2 * u + 1; }
	inline int rs(int u) { return 2 * u + 2; }
	
	struct SegTree {
		vector<Node> tr;
		
		inline SegTree() {}
		inline SegTree(const vector<int>& a) {
			const int n = a.size();
			tr.resize(n << 1);
			build(0, 0, n - 1, a);
		}
		
		inline void pushup(int u, int mid) {
			tr[u].val = tr[ls(mid)].val + tr[rs(mid)].val;
		}
		
		inline void apply(int u, const Mat& mat) {
			tr[u].val = tr[u].val * mat;
			tr[u].tag = tr[u].tag * mat;
		}
		
		inline void pushdown(int u, int mid) {
			if (tr[u].tag == Mat()) return;
			apply(ls(mid), tr[u].tag);
			apply(rs(mid), tr[u].tag);
			tr[u].tag = Mat();
		}
		
		void build(int u, int l, int r, const vector<int>& a) {
			tr[u].l = l, tr[u].r = r;
			if (l == r) {
				tr[u].val[0][0] = 1;
				tr[u].val[0][1] = add(a[l], mod);
				tr[u].val[0][2] = tr[u].val[0][3] = 1LL * a[l] * a[l] % mod;
				return;
			}
			const int mid = (l + r) >> 1;
			build(ls(mid), l, mid, a);
			build(rs(mid), mid + 1, r, a);
			pushup(u, mid);
		}
		
		void modify(int u, int l, int r, const Mat& mat) {
			if (l <= tr[u].l && tr[u].r <= r) return apply(u, mat);
			const int mid = (tr[u].l + tr[u].r) >> 1;
			pushdown(u, mid);
			if (l <= mid) modify(ls(mid), l, r, mat);
			if (r > mid) modify(rs(mid), l, r, mat);
			pushup(u, mid); 
		}
		
		Mat query(int u, int l, int r) {
			if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
			const int mid = (tr[u].l + tr[u].r) >> 1;
			Mat res = Mat(0);
			pushdown(u, mid);
			if (l <= mid) res = res + query(ls(mid), l, r);
			if (r > mid) res = res + query(rs(mid), l, r);
			return res;
		}
		
		inline void range_mul(int l, int r, const Mat& mat) { modify(0, l, r, mat); }
		inline Mat range_sum(int l, int r) { return query(0, l, r); }
	};
}
using seg_tree::SegTree;

struct Query {
	int l, r, t, id;
	inline Query() {}
	inline Query(int _l, int _r, int _t, int _id) : l(_l), r(_r), t(_t), id(_id) {}
};

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	const int n = read<int>(), m = read<int>(), q = read<int>();
	vector<int> a(n), sum(n);
	
	for (int i = 0; i < n; i++) {
		a[i] = read<int>();
		sum[i] = add(i ? sum[i - 1] : 0, 1LL * a[i] * a[i] % mod);
	}
	
	vector<int> L(m), R(m), X(m);
	for (int i = 0; i < m; i++) {
		L[i] = read<int>(), R[i] = read<int>(), X[i] = read<int>();
		L[i]--, R[i]--;
	}
	
	vector<vector<Query>> adj(m + 1);
	for (int i = 0, l, r, x, y; i < q; i++) {
		l = read<int>(), r = read<int>(), x = read<int>(), y = read<int>(), l--, r--;
		adj[y].emplace_back(l, r, 1, i);
		if (x > 0) adj[x - 1].emplace_back(l, r, -1, i);
	}
	
	Mat A; A[2][3] = 1;
	SegTree sgt(a);
	vector<int> ans(q);
	for (auto & [l, r, t, id] : adj[0]) {
		ans[id] = add(ans[id], add(add(sum[r] - (l > 0 ? sum[l - 1] : 0), mod) * t, mod));
	}
	
	for (int i = 0; i < m; i++) {
		Mat tmp;
		tmp[0][1] = add(X[i], mod);
		tmp[0][2] = tmp[0][3] = 1LL * X[i] * X[i] % mod;
		tmp[1][2] = tmp[1][3] = 2LL * add(X[i], mod) % mod;
		tmp[2][3] = 1;
		
		sgt.range_mul(L[i], R[i], tmp);
		if (L[i] > 0) sgt.range_mul(0, L[i] - 1, A);
		if (R[i] < n - 1) sgt.range_mul(R[i] + 1, n - 1, A);
		
		for (auto & [l, r, t, id] : adj[i + 1]) {
			ans[id] = add(ans[id], add(sgt.range_sum(l, r)[0][3] * t, mod));
		}
	}
	for (int i = 0; i < q; i++) {
		write(ans[i]);
		putchar_unlocked('\n');
	}
	
	return 0;
}
相关推荐
青草地溪水旁8 分钟前
设计模式(C++)详解——解释器模式(2)
c++·设计模式·解释器模式
Kevinhbr28 分钟前
CSP-J/S初赛赛后总结
c++·程序人生·ccf csp-j/s
MoRanzhi120334 分钟前
9. NumPy 线性代数:矩阵运算与科学计算基础
人工智能·python·线性代数·算法·机器学习·矩阵·numpy
Zewen PAN1 小时前
新手 Visual Studio 环境配置 详解
c++·ide·visual studio
aaaaaaaaaaaaay1 小时前
代码随想录算法训练营第五十一天|99.岛屿数量 深搜 99.岛屿数量 广搜 100.岛屿的最大面积
算法·深度优先
hn小菜鸡1 小时前
LeetCode 2460.对数组执行操作
算法·leetcode·职场和发展
.YM.Z2 小时前
数据结构——链表
数据结构·链表
jghhh012 小时前
HT16C21 驱动模拟I2C实现
单片机·嵌入式硬件·算法
自信的小螺丝钉2 小时前
Leetcode 148. 排序链表 归并排序
算法·leetcode·链表·归并
listhi5202 小时前
基于梯度下降、随机梯度下降和牛顿法的逻辑回归MATLAB实现
算法·matlab·逻辑回归