毎日格上の問題を倒すやつの44日目です。
問題リンク
問題概要
以下の条件をすべて満たす整数の組を考えるよ。
あり得るすべての組に対して、 の総和を求めてね。
解法
以下の記事にFPSの基礎が詳しく載っています。
私もこれを参考に解いたので、最後の演習の私の答えを載せるにとどめておきます。
11.
手計算すれば であることが容易にわかります。この時点で規則性を見つけておくと楽でしょう。
12.
先ほどの数列の階差を見てみると、 となります。慣れている人はこの時点で母関数を求められそうですが、少し難しいのでもう一回階差をとります。すると、 となります。
これは簡単な話で、漸化式 と表せるため、母関数は と表せます。
あとは2回階差をとったので2回累積和をとることで元の形に直します。 すると、 と表せます。
この場合 となってしまいますが、この後何か困ることはほとんどないので気にしないことにします。
13.
この関数を5乗すると、和がちょうど の場合は解けます。
しかし、今回は「以下」を求めたいです。
これを1から愚直に求めると結局 かかってしまいます。
そのため、 で割って累積和を求めましょう。こうすると一か所を見るだけでよくなります。
答えは となります*1。 は先ほど とした弊害で、0次目が のときの解となっているためです。そのため-5しています。*2
14.
あとは展開して行列累乗なりBostan-moriで殴れば終わりです。
今回の母関数は約 次です。これを として、
- 行列累乗...
- 愚直Bostan-mori...
- 任意mod畳み込み+Bostan-mori...
となります。どれでも余裕をもって間に合いますが、提出コードは愚直Bostan-moriです。
提出コード
def bostan_mori(N,P,Q,MOD): while N>0: U=[0]*(len(P)+len(Q)-1) for i in range(len(P)): for j in range(len(Q)): if j%2==0: U[i+j]+=P[i]*Q[j] else: U[i+j]-=P[i]*Q[j] U[i+j]%=MOD P=U[N%2::2] V=[0]*(len(Q)*2-1) for i in range(len(Q)): for j in range(len(Q)): if j%2==0: V[i+j]+=Q[i]*Q[j] else: V[i+j]-=Q[i]*Q[j] V[i+j]%=MOD Q=V[0::2] N//=2 return P[0]//Q[0]%MOD T=int(input()) for _ in range(T): N=int(input()) if N<5: print(0) continue print(bostan_mori(N-5,[1],[1, -11, 50, -110, 65, 253, -648, 440, 610, -1430, 780, 780, -1430, 610, 440, -648, 253, 65, -110, 50, -11, 1],10**9+7))
Submission #47166313 - AIsing Programming Contest 2020
感想
非人間的なDPもFPSで導出できるのおもしろすぎる…