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 aiai+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 alar 在第 [ 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 1n,m,q5×104
1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1lrn
0 ≤ L ≤ R ≤ m 0\le L\le R\le m 0LRm
∣ 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,x1],在时间轴上做扫描线.
然后需要一个 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)

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值