公式

F - Two Sequence Queries 解説 by mechanicalpenciI


この問題を遅延セグメント木で解くことを考えます。
遅延セグメント木の詳細については、他の解説記事やAtCoder Library の説明などを参照してください。

具体的には、\(a,k\geq 0\), \(2^k(a+1)\leq N\) をみたす整数 \(a,n\) を用いて \(a\cdot2^k+1\leq i\leq (a+1)2^k\) と書ける区間に対して、区間内の \(A_i\times B_i\) の総和を管理し、その区間全体への \(A_i, B_i\) への加算操作を行ったときのを変化を高速に(基本的には \(O(1)\) の計算量で)計算したいです。

操作の集合としては、非負整数 \(x,y\) について、「(その区間全体の) \(A\) の要素に \(x\) を加え、 \(B\) の要素に \(y\) を加える。」といった操作全体を考えます。これは、クエリで与えられる操作を含んでおり、さらに合成について閉じています。また、\(x=y=0\) とすれば操作の集合を写像の集合とみなしたときの単位元となります。

さて、上で述べた各区間 \(a\cdot2^k+1\leq i\leq (a+1)2^k\)(以下では、\(L=a\cdot2^k+1\), \(R=(a+1)2^k\) とします)の \(A_i\times B_i\) の総和 \(\displaystyle\sum_{i=L}^R (A_i\times B_i)\) を操作によって更新するにあたって、各区間について \(\displaystyle\sum_{i=L}^R (A_i\times B_i)\) の値のみを保持するだけでは不十分です。
なぜなら、

\[ \displaystyle\sum_{i=L}^R ((A_i+x)\times (B_i+y)) =\displaystyle\sum_{i=L}^R (A_i\times B_i) +y\displaystyle\sum_{i=L}^R A_i +x\displaystyle\sum_{i=L}^R B_i +(R-L+1)xy \]

となるため、区間に対して管理している情報 \(\displaystyle\sum_{i=L}^R (A_i\times B_i)\) および操作の情報 \(x,y\) が与えられたとしても更新先の値は一意に定まらないからです。

そこで、各区間について 区間の長さ \((R-L+1)\) および \(\displaystyle\sum_{i=L}^R A_i\), \(\displaystyle\sum_{i=L}^R B_i\), \(\displaystyle\sum_{i=L}^R (A_i\times B_i)\)\(3\) 種類の総和を管理することを考えます。このとき、上の式から操作による\(\displaystyle\sum_{i=L}^R (A_i\times B_i)\) の値の更新を \(O(1)\) で行うことができます。また、

\[ \displaystyle\sum_{i=L}^R (A_i+x) =\displaystyle\sum_{i=L}^R A_i+(R-L+1)x \\ \displaystyle\sum_{i=L}^R (B_i+y) =\displaystyle\sum_{i=L}^R B_i+(R-L+1)y \]

より、これらの値も操作に対する更新を \(O(1)\) で行うことができます。区間の長さは操作によって変わることはありません。また、区間の連結と更新操作が可換となるような、区間の連結に伴う(区間ごとに管理されている)情報の和の定め方については、それぞれの長さおよび総和の和を取れば良いです。また長さ \(0\), すべての総和が \(0\) であるようなものを考えれば それが和についての単位元となります。

よって、遅延セグメント木によって、答えを求めることができます。このとき計算量は \(O(Q\log N)\) であり、十分高速です。
よって、この問題を解くことができました。

\(998244353\) で割った余りを出力することに注意してください。

#include <bits/stdc++.h>
#include <atcoder/lazysegtree>
#include <atcoder/modint>

using namespace std;
using namespace atcoder;

using mint = modint998244353;


struct S {
    mint len,Asum,Bsum,ABsum;
};
struct F {
    mint Aadd,Badd;
};

S op(S l, S r) {
    return S{
        l.len + r.len,
        l.Asum + r.Asum,
        l.Bsum + r.Bsum,
		l.ABsum + r.ABsum,
    };
}
S e() { return S{mint(0),mint(0),mint(0),mint(0)}; }
S mapping(F op, S tgt) {
    return S{
		tgt.len,
		tgt.Asum+(op.Aadd*tgt.len),
		tgt.Bsum+(op.Badd*tgt.len),
		tgt.ABsum+(op.Aadd*tgt.Bsum)+(op.Badd*tgt.Asum)+(op.Aadd*op.Badd*tgt.len)
	};
}
F composition(F l, F r) { return F{l.Aadd+r.Aadd,l.Badd+r.Badd}; }
F id() { return F{mint(0),mint(0)}; }

int main() {
	int n,q;
	int t,l,r,x;

	cin>>n>>q;
	vector<int>a(n),b(n);
	vector<S>tmp(n);

	for(int i=0;i<n;i++)cin>>a[i];
	for(int i=0;i<n;i++)cin>>b[i];
	for(int i=0;i<n;i++)tmp[i]=S{mint(1),mint(a[i]),mint(b[i]),mint(a[i])*mint(b[i])};
	lazy_segtree<S, op, e, F, mapping, composition, id> seg(tmp);

	for(int i=0;i<q;i++){
		cin>>t>>l>>r;
		if(t<3)cin>>x;
		if(t==1)seg.apply(l-1,r,F{mint(x),mint(0)});
		if(t==2)seg.apply(l-1,r,F{mint(0),mint(x)});
		if(t==3)cout<<(seg.prod(l-1,r).ABsum.val())<<endl;
	}	
	return 0;
}

投稿日時:
最終更新: