エクサウィザーズ2019-D:Modulo Operations

めちゃくちゃ時間をかけてしまったけどこういうのが通せるようになったのは成長かな...。

考察過程・解法

 確率に落とすのは苦手なので総和のままやった。
 まず最初に,s_i\lt s_jという2つの集合の要素について,s_iで余りをとった値に,s_jで余りをとってもその値は変わらないことに気付こう。したがって,sをソートして新たに番号を振り直すことにする。すると,s_1,s_2,\cdots,s_iを作ってできた値をvとすると,vs_{i+1},\cdots,s_Nたちの影響を受けない。このことを用いて,dpを組み立てていこう。
 dp_{i,j}を「黒板にjが書かれている状態から始めて,s_1,s_2,\cdots,s_iのみを選んで操作をしたときの,最後に黒板に書かれる数字の総和」としよう。こうすると,dp_{i-1,X (\mathrm{mod} s_i)}\times {}_{N-1} \mathrm{P} _{N-i}iについての総和が答えとなる。この理由を説明しよう。s_iを先頭にすると決め打つと,次に書かれる数はX (\mathrm{mod} s_i)となる。そこからの操作はどうなるかというと,s_1からs_{i-1}までの順番はdpの部分に表現されているから,残りのN-i個の並べ方だけが自由である。従って,上の式が「s_iを最初に選んだ時の,最後に黒板に書かれる数の総和」を表しており,これをi=1,2,\cdots,Nについて足し合わせれば,すべての順列について考えたことになる。
 最後にdpの遷移を考えよう。jが黒板に書かれているとして,使える数がs_{i-1}までからs_iまでに増えたときに,どのような操作が可能になるだろうか?まず,最初にs_iを選ぶとすると,黒板の数はj (\mathrm{mod} s_i)となるから,その後の操作によってできる数の総和はもちろんdp_{i-1,j (\mathrm{mod} s_i)}となる。最初にs_{i-1}以下の数を選ぶとすると,その後の操作によってできる数の総和はdp_{i-1,j}\times (i-1)となる。i-1がかかっているが,最初にs_{i-1}以下の数を選んだので,s_iはどのタイミングで選んでも総和には影響しない。よって,s_iを入れるタイミングの数だけdp_{i-1,j}が加算されるということになる。
 計算量はO(NX)であり,C++なら十分間に合う。Pythonだと心配。

実装

Submission #4777838 - ExaWizards 2019

i=1のときだけ注意。もうちょっとうまく書けるかも。

最近調子が良いので黄色が見えてきた。