目次
まとめ:まず、形式的冪級数を用いて問題を整理し、合成関数を求める問題に帰着します。ここで求めたい合成関数 A(B(x)) の中身 B(x) が特殊な形をしていることを利用して、高速に合成を求めることができます。 O(NlogN) または O(Nlog2N) 時間でこの問題に正解することができます。
- 目次
- 考察
- 導入
- B(x) の表示
- D(x) の表示
- まとめ
- O(NlogN) の方法…想定解(Pypy3 でも通る)
- O(Nlog2N) の方法 1…想定解
- O(Nlog2N) の方法 2…新しい解法(合成を求める)
- O(Nlog2N) の方法 3…新しい解法
考察
導入
問題の答えを a1 とします。操作の 「−1,+1 から等確率で選ぶ」の部分を「−1,+1 のどちらか一方を選ぶ」に変更したとき、ありえる操作すべてに対するスコアの総和を a2 とします。このとき、 a1=a2/2N であることが分かります。 1/2Nmod998244353 は容易に計算できるため、代わりに a2 を計算することを考えます。
しょぼん君の操作による動きをパスと呼ぶことにします。次のように形式的冪級数を定めます。
- B(x) は、座標 0 から出発し n ステップ目で座標 0 に初めて戻ってくる長さ n のパスの個数が xn の係数になるような形式的冪級数
- D(x) は、座標 0 から出発して座標 0 に一度も戻らないような長さ n のパスの個数が xn の係数になるような形式的冪級数
- A(x) は、 A(x)=A0+A1x+⋯+ANxN を満たす多項式
- F(x) は、長さ n のパスすべてに対するスコアの合計が xn の係数になるような形式的冪級数
求めたいものは [xN]F(x) です。ここで、「座標 0 にちょうど k 回戻ってくる」、すなわちスコア Ak を獲得できるようなパスの個数の母関数は
B(x)kD(x)
と表されるため、 F(x) は次のように表せます。
F(x)=A0D(x)+A1B(x)D(x)+A2B(x)2D(x)+⋯+ANB(x)ND(x)=D(x)A(B(x))
B(x) の表示
B(x) と D(x) を具体的に表示してみます。 B(x) は「座標 0 から出発し、 0 に初めて戻って来る」というものでした。もし 1 回目で座標 1 に行ったとすると、2 から n−1 回目では座標 1 以上のみを動き、n 回目で座標 1→0 にもどってくる、ということになります。そして、 1 回目で座標 −1 に行ったときも同様です。
2 から n−1 回目の全 n−2 回において、これは「座標 0 からスタートし、座標 0 以上を n−2 回動き、座標 0 に戻ってくる」方法の数と同じで、この場合の数はカタラン数と同じです。 n
が奇数のときは 0 通り、偶数のときは (n−2)/2+11((n−2)/2n−2) となります。
カタラン数の母関数 C(x) を
C(x)=∑n≥0n+11(n2n)xn=1+x+2x2+5x3+14x4+42x5+⋯
とおくと、B(x) は
B(x)=2x2C(x2)=2x2+2x4+4x6+10x8+28x10+84x12+⋯
と表されます。ここで、カタラン数の母関数は
C(x)=2x1−1−4x
であるので、
B(x)=2x2C(x2)=1−1−4x2
が分かりました。
D(x) の表示
次に、D(x) について、余事象は「0 からスタートし、 0 に戻ってくる瞬間が存在する」ですが、 0 に初めて戻ってくるのが何回目であるかで場合分けすれば、
[xn]D(x)=2n−[xn]B(x)−2[xn−1]B(x)−⋯−2n[x0]B(x)
が得られます。これはとくに、
[xn]D(x)=2n−[xn]1−2xB(x)=[xn]1−2x1−B(x)
であるため、
D(x)=1−2x1−4x2
が分かります。
まとめ
以上を整理すると、求めるべきものは
[xN]F(x)=[xN]D(x)A(B(x))=[xN]1−2x1−4x2A(1−1−4x2)
になります。
O(NlogN) の方法
前半戦
今回の場合、合成関数の中身 1−1−4x2 が簡単であるため、合成関数を直接「ほどく」イメージで求められます。
A(1−1−4x2) に対して次のように式変形します。
A(1−1−4x2)=i=0∑NAi(1−1−4x2)i=i=0∑NAik=0∑i(ki)(−1−4x2)k=k=0∑N(−1−4x2)ki=k∑NAi(ki)=k=0∑N(−1−4x2)k[xk](i=0∑NAi(1+x)i)
ここで、 ∑i=0NAi(1+x)i=P0+P1x+⋯+PNxN とおきます。そうすると、
A(1−1−4x2)=k=0∑NPk(−1−4x2)k
です。そして、 P0,P1,⋯,PN は次のように O(NlogN) 時間で求められます:
I(x)=∑i=0NAixi
とおきます。このとき、求めたい多項式は I(x+1) であり、その各項は Polynomial Taylor Shift というテクニックを用いて求められます。
Polynomial Taylor Shift については、 ABC215-G のユーザー解説 (MMNMM さん) をご覧ください。
後半戦
A(1−1−4x2)=k=0∑NPk(−1−4x2)k
の各係数を求めたいです。 k の偶奇で分け、
k=0∑NPk(−1−4x2)k=k=0∑⌊N/2⌋P2k(1−4x2)k−1−4x2k=0∑⌊(N−1)/2⌋P2k+1(1−4x2)k
とします。ここで、
∑k=0⌊N/2⌋P2k(1−4x2)k=X0+X1x+⋯+XNxN
∑k=0⌊(N−1)/2⌋P2k+1(1−4x2)k=Y0+Y1x+⋯+YNxN
とおくと、
A(1−1−4x2)=(X0+X1x+⋯XNxN)−1−4x2(Y0+Y1x+⋯YNxN)
となります。いま、 Xi,Yi の各項をすべて求めることは、同じく Polynomial Taylor Shift によって O(NlogN) 時間で出来ます。
そして、 1−4x2=(1−4x2)1/2 は疎な形式的冪級数の pow であるので、xN までの全ての項が O(N) 時間で計算できます。あとは 1−4x2 と Y0+Y1x+⋯+YNxN との畳み込みを行えば、 A(1−1−4x2) の各項が計算できました。
疎な形式的冪級数のpowを高速に求めるテクニックについては A problem collection of ODE and differential technique (jqdai0815 さん) の「Exp in Gennady Korotkevich Contest 5 By tourist」の節をご覧ください。
最後に、 D(x)A(1−1−4x2) が求める答えです。D(x)=1−2x1−4x2 は 1−4x2 の累積和を求めるのと同じようにして O(N) 時間で求められます。その後、 D(x) と 1−4x2 との畳み込みを行えばよいです。以上から、合わせて O(NlogN) 時間で解けます。
O(Nlog2N) の方法
漸化式・行列
B(x) はカタラン数とほぼ同じです。カタラン数の性質を用いることを考えます。カタラン数の母関数 C(x) について、
xC(x)2=C(x)−1
が成り立ちます。これを用いると、 B(x)=2x2C(x2) について
B(x)2=4x4C(x2)2=4x2(C(x2)−1)=2B(x)−4x2
が成り立つことが分かります。求めるべきは
A(B(x))=i=0∑NAiB(x)i
です。これから、先ほどの漸化式を用い、分割統治を使ってこの式を高速に求めます。
多項式 Pk(x),Qk(x) を用いて B(x)k=Pk(x)B(x)+Qk(x) とおくと、
i=0∑NAiB(x)i=i=0∑NAi(PiB(x)+Qi)
となります。これを高速に求めることができるでしょうか?(Pi,Qi) の組から (Pi+1,Qi+1) の組への遷移は次のようになります。
B(x)i+1=B(x)B(x)i=B(x)(PiB(x)+Qi)=Pi(2B(x)−4x2)+QiB(x)=(2Pi+Qi)B(x)−4x2Pi
よって、多項式を係数とした行列を用いて
(Pi+1Qi+1)=(2−4x210)(PiQi)
と表されます。とくに、
i=0∑NAiB(x)i=i=0∑NAi(B(x)1)(2−4x210)i(01)=(B(x)1)(i=0∑NAi(2−4x210)i)(01)
であるため、i=0∑NAi(2−4x210)i を求めればよいです。
分割統治
f(L,R)=i=L∑R−1Ai(2−4x210)i−L
とします。求めるものは f(0,N+1) です。
まず、 R−L=1 の場合は f(L,R)=(AL00AL) です。
そうでない場合、 ⌊(L+R)/2⌋=M として、f(L,M),f(M,R) が求まったとします。このとき、 f(L,R) が次のように計算できます:
f(L,R)=i=L∑R−1Ai(2−4x210)i−L=i=L∑M−1Ai(2−4x210)i−L+(2−4x210)M−Li=M∑R−1Ai(2−4x210)i−M=f(L,M)+(2−4x210)M−Lf(M,R)
ここで、 (2−4x210)M−L と f(M,R) との畳み込みで O(NlogN) 時間かかります。
(2−4x210)M−L については同じく分割統治で求められます。
この分割統治は f(L,R) の統治の部分に O((R−L)log(R−L)) 時間かかるので、 f(0,N+1) を求めるのに計算量は O(Nlog2N) 時間かかります。
これで合計 O(Nlog2N) 時間でこの問題が解けました。
定数倍高速化
このままでは定数倍が重く、C++ であっても通すのが難しいです。しかし、次のような定数倍改善がいくつか考えられ、これらを使って AC を獲得することができます。(1, 2, 3 番目のどれかを用いるのが簡単です)
- 原点に戻ってくる回数は高々 ⌊N/2⌋ 回であるため、A を A0,⋯,A⌊N/2⌋+1 しかないことにしてよい。
- A の長さを 2 べき、すなわちある非負整数 k が存在して AN+1=⋯=A2k−1=0 とする。これによって、分割統治の際 (2−4x210)M−L における M−L がすべて 2 べきになる。(2−4x210)1,(2−4x210)2,(2−4x210)4,⋯,(2−4x210)2i,⋯ は分割統治をせずに合計 O(NlogN) で求めることができる。
- (2−4x210) の代わりに (2−4x10) を用いて計算してよい。後で x を x2 に置き換えれば求めたいものが求まる。
- 行列積を計算する際、FFT を 8 回行うと butterfly が(inv含めて)合計 16 回行われるが、butterfly の回数を 8 回に削減することが可能である。A の長さを 2 べきにすることで、(2−4x210)2i の butterfly が再利用でき、さらに定数倍改善することも可能である
O(Nlog2N) の方法 2
noshi91 さんが2024年3月16日に投稿した記事 FPS の合成と逆関数、冪乗の係数列挙 Θ(n (log(n))^2) で、合成関数を求める操作が O(Nlog2N) 時間で出来ることが示されています。そして、実際に合成を O(Nlog2N) 時間で行うアルゴリズムによって、この問題を AC できます。
O(Nlog2N) の方法 3
noshi91 さんが2024年3月16日に投稿した記事 FPS の合成と逆関数、冪乗の係数列挙 Θ(n (log(n))^2) と同じ方法で、固定された k に対して [xk]a(x)b(x)i をすべての 0≤i<n に対して列挙することが O(nlog2n+klog2k) 時間で可能です。 2 変数形式的冪級数 F[[y]][[x]] において [xk]1−yb(x)a(x) の yn 次までを求めればよく、bostan-mori 法を用いて求められます。(詳しくは記事をご覧ください)
今回、求めたいものは
[xN]D(x)A(B(x))=i=0∑NAiD(x)B(x)i
でした。よって、[xN]1−yB(x)D(x) を yN まで求めればこの問題を O(Nlog2N) 時間で解くことができます。
定数倍高速化
この方法だと定数倍が重く、実行時間制限に間に合わない可能性があります。そこで、次の ad-hoc な定数倍高速化が考えられます。やることは bostan-mori 法の一部分とほとんど同じです。
- この問題の場合、 B(x) は偶関数であるので、 B(x)=V(x2) となる形式的冪級数 V が存在する。また、 D(x) を 2 つの偶関数 De(x2),Do(x2) に分け、 D(x)=De(x2)+xDo(x2) とおく。このとき
[xN]1−yB(x)D(x)=[xN]1−yV(x2)De(x2)+xDo(x2)={[xN]1−yV(x2)De(x2)[xN]1−yV(x2)xDo(x2)(Nは偶数)(Nは奇数)={[xN/2]1−yV(x)De(x)[x(N−1)/2]1−yV(x)Do(x)(Nは偶数)(Nは奇数)
が成り立つ。また、実際には y は yN/2 程度までしか列挙する必要がない。最初にこの処理をしておくと、 2 倍以上の定数倍高速化となる。