注意事項
対象の読者として、「累積和について一応わかっていて、単純な累積和の問題は解ける」くらいを想定しています。
「累積和って何?初めて聞いたよ!」という人は読んでもキツいと思います。
また、やや抽象的な議論になります。
競技プログラミングには、累積和の他にも、累積××というような解法が存在する。
AtCoderの以下の問題では「累積GCD」という解法が登場した(GCD=最大公約数)。
C - GCD on Blackboard
あとは累積XORを使う問題も存在する。
累積和、累積積、累積xor、累積gcd...
— prd🦍 (@prd_xxx) April 27, 2019
他に何が累積できるんだろう。
累積hoge まとめ
— ⚡データ構造から逃げない⚡ (@0214sh7) April 27, 2019
累積和:いろいろ
累積GCD:GCD on Blackboard
累積XOR:Xor Sum 2
累積和・累積GCD・累積XORという手法があるが、それぞれをバラバラに考えていると良くない。
仮に、他の演算に対して累積演算を考えて解かなければいけない問題があったとすると、
解法に全く気づかずに「しまったー!これは累積ナントカだったのか!」というハメになる。
そこで、個々の演算を離れて抽象化をし、どういう演算について「累積××」ができるのだろうか、を考える。
まぁ、下のツイート(俺じゃないよ)の内容を詳しく書き起こしたようなものなので、このツイートを読んで納得した人は続きを読まなくても良いかもしれない……。
「累積GCD」より、「累積テーブルは逐次的に処理してく演算全てに対して使え、逆から累積和をあわせるテクは結合法則が成立する演算に使える」みたいに理解して欲しさがあります
— keymoon (@kymn_) April 27, 2019
累積××の適用方法には2つのパターンが存在する。以下、パターン1・2と勝手に名付ける。
パターン1:逆演算が定義できなくても、「最初から途中まで」の累積演算ができる
逆演算っていうの?逆作用素なの?……それがない場合には、「最初から途中まで」の累積演算ができる。
を一般の(何らかの)演算子とする。 例えば累積和の場合はは+(加算演算子)だし、累積XORの場合はXOR(ビットXOR、ビットごとの排他的論理和)である*1。
で、を求めることを考える。 このとき、
で定まるb_nを考えよう。 と計算することで、b_1からb_nまでを計算時間O(n)で計算できる。
そうすると、b_nを何度も使う場合には計算量が削減できる。
逆側からの累積と合わせ技
GCD on Blackboard問題の場合は上記のb_nだけでは不十分である。
で定まる「逆側からの累積GCD」も同時に考える必要がある。こちらは「途中から最後まで」の累積演算になる。
これはどういう演算子に対して使用可能か?
GCD on Blackboardの解説PDFには登場しているのだが、これをするためには演算子が結合法則
を満たしている必要がある。
簡単に言えば、gcdには「どこから計算しても結果は変わらない性質」があります
GCD on Blackboardの解説PDF
gcdはこれを満たしている。 すなわち、gcd(gcd(a1, a2), a3)= gcd(a1, gcd(a2, a3)) が成り立つ。
他には……bitのand / or、最大値/最小値なんかは結合法則を満たす。行列積も結合法則を満たす。 一方、減算や除算は結合法則を満たさないので、この手法が使えない。
そうすると……適当に作った問題だけど、こんな感じかな。(問題の細かい条件は割愛)
黒板に1列に数列a1, a2, …… , aNが書いてあります。 これに対して、 s1, e1 s2, e2 …… sM, eM という入力が与えられます。 M個の数を出力しなさい。 i番目(1 ≦ i ≦ M)の出力では、 数列が全て書いてある状態から、si番目からei番目までの数を消したときに、黒板に残った数の最大値を出力しなさい。
とかいう問題が出てきたら、累積最大値を使えば計算量O(N+M)で解けそうだ。(GCD on Blackboardとほとんど同様にして解ける。)
パターン2:逆演算があれば、「任意の連続部分」の累積演算ができる
パターン1ではbnとcn、すなわち「最初から途中まで」「途中から最後まで」だけを計算できた。
「途中から途中まで」すなわち「任意の連続部分」に関して、累積の演算結果を求められるのがパターン2である。
パターン1の特別な場合がパターン2である。
の逆演算 が存在する場合、例えば
が必要だったら、
とすればよい。一般の連続区間についても同様である。
パターン2では単にb_nだけを先に計算すればよく、c_nはもはや必要ない(b_nから直ちに計算できる)。
これはどういう演算子に対して使用可能か?
このパターンは逆演算 (逆作用素)があることが必要である。
直前の書き方は、実は正確ではない(一般の演算子について成り立たない)。 正しい求め方は、eを演算に対する単位元として
(結合法則より)
(逆演算の性質より)
(単位元の性質より)
だと思う。
GCDはこの性質を満たさない(逆演算子が存在しない)ので、パターン2の解法は使えない。
累積和(加算)の場合は が-になる。 XORの場合は もXORである。(a XOR b) XOR b = aが成り立つからだ。
累積XORについては 競技プログラミングにおけるXORのTips - Qiita でも紹介されている。
あとは上のツイートにもあったが、積(乗算)が同様にできるのも分かるだろう(ただし0との乗算は除外する)。
他に同様の性質を満たす演算はあるだろうか?
結合法則・単位元・逆元を満たす演算を、代数学では群と呼ぶ。
群 (数学) - Wikipediaを見ながら思い出すと、例えば、2次正方行列で逆行列が存在するものの集合は群となる。
よって、そのような行列A_iに対して、例えば を計算したい場合、 をあらかじめ計算しておけばB5にB2の逆元を左からかけることで
となって求めることができる。
なお、行列乗算は交換法則を満たさないので、右から乗算すると失敗する。(上で「実は正確ではない(一般の演算子について成り立たない)」とか書いていたのは、交換法則を満たさない場合の話である。)
……だけどこんな行列積の計算、競技プログラミングにも絶対出てこないだろ。
というわけで今日はここまで。
それでは。
*1:排他的論理和 - Wikipediaを見たらXORの代わりにこの記号を使う場合もあるらしい。マジか。紛らわしくてごめんなさい、今回はXORと無関係な一般の演算子のことを指している