注意:yukicoderやその他のサイトの問題に対するネタバレがある可能性があります
以下では多項式は可換環であるRを係数とするとする。
計算量はR上の基本演算が定数時間でできるとして考える。ただしあまり厳密ではない。
競技プログラミングにおいてはたいていワードサイズのmodを取るので定数時間で計算できるとして問題ない事が多い。
多項式同士の乗算の計算量をM(n)とする。多項式除算もO(M(n))で計算できる。
単純なアルゴリズムではM(n)∈O(n2)だが、R=Z/mZの場合はFFTを用いることでM(n)∈O(nlognloglogn)にできる。
多項式の総和
pをk次の多項式とするとき、P(n)=∑i=0n−1p(i)はk+1次以下の多項式となる。
∑i=0n−1i=2n(n−1)などの公式として有名だろう。
多項式のある種の逆元
有名なpower seriesの式∑i=0∞ai=1−a1は多項式にも適用できる。
つまり、多項式f=∑i=0kaiXiに対し、ある種の逆元を∑i=0∞(1−f)iと表すことができる。
この式はa0=1である場合に限りよく定義でき、これは多項式環の拡張である"formal power series"環上に値を取る。
fにa0−1かけることで、つまりf−1=a0−1∑i=0∞(1−a0−1f)iとして、a0∈R∗である限りこれを求めることができる。
多項式の平行移動
f(X):=∑i=0NaiXiを
Cだけ平行移動した
f(X+C)の係数を
O(NlogN)で求める。二項展開して地道に計算することで
\begin{align}
f(X+C)=&\sum_{i=0}^N a_i (X+C)^i \\
=&\sum_{i=0}^N a_i \sum_{j=0}^i \binom{i}{j}X^jC^{i-j}\\
=&\sum_{i=0}^N X^i \sum_{j=i}^N a_j \binom{j}{i} C^{j-i}\\
=&\sum_{i=0}^N \frac{X^i}{i!} \sum_{j=i}^N a_j \frac{j!}{(j-i)!} C^{j-i}\\
=&\sum_{i=0}^N \frac{X^i}{i!} \sum_{k=0}^{N-i} a_{k+i}(k+i)! \frac{C^{k}}{k!} \\
=&\sum_{i=0}^N \frac{X^i}{i!} [s^{-i}] g(s)\exp(Cs) \\
\end{align}
を得る。ただし
g(s):=∑i=0Naii!s−i,exp(Cs):=∑i=0Ni!(Cs)iとする。
g(s)と
exp(Cs)の畳み込みになっているのでFFTで
O(NlogN)で計算できる。
これは平行移動後の関数をラプラス変換したものの逆ラプラス変換
Θ(X+C)∑k=0Nfk(X+C)k=L−1[exp(Cs)∑k=0Naksk+1k!]を計算しているとみなせる(?)。
Πi=1N(1+Xsi)
分割数を計算するときに
Πi=1N(1+Xsi)を計算したくなることがある。
https://arxiv.org/abs/1807.11597にて
A(X):=Πi=1N(1+Xsi)modXt+1を
O(tlogt)で求めるアルゴリズムが示されている。
log(1+X)=∑i=1∞i(−1)i−1Xiであるから
\begin{align}
\log\Pi_{i=1}^{N} (1+X^{s_i})=&\sum_{i=1}^N\log(1+X^{s_i}) \bmod X^{t+1}\\
=&\sum_{i=1}^N\sum_{j=1}^\infty \frac{(-1)^{j-1}}{j}X^{js_i} \bmod X^{t+1}\\
=&\sum_{i=1}^t \sum_{j=1}^{[t/j]} a_i\frac{(-1)^{j-1}}{j}X^{j i} \bmod X^{t+1}
\end{align}
ただし、
ai:=#{j:sj=i}とする。
O(∑j=1t[t/j])=O(tlogt)で計算できる。あとは
A(X)=exp(log(A(X))から計算すればよい。
演習問題
母関数
数列に対し、有理関数が母関数であることと線形漸化式であることは同値である。
・線形漸化式⇒母関数は有理関数
数列{an}n≥0がn≥Mのとき漸化式an=∑k=1mαkan−k で表されているとする。(ただしαkは定数かつαm=0)
このとき母関数をfとして h(x):=1−∑k=1mαkxk,g:=hfとする。
gの各次数の係数について考えれば、M次以上は全て相殺されるのでgは高々M−1次多項式。
hは定義より明らかにm次多項式なのでf=g/hは有理関数。
・母関数が有理関数⇒線形漸化式
有理関数fを母関数とする数列{an}n≥0を考える。
fは有理関数なので多項式g,hによりf=g/hと表せる。
hf=gの両辺をn(>max{deg g,deg h})回微分するとライプニッツ則により
∑k=0deg h(kn)f(n−k)(x)h(k)(x)=0 ((kn)は二項係数)
となるのでx=0を代入し、k=0の項を残して移項すると
f(n)(0)h(0)=−∑k=1deg h(kn)f(n−k)(0)h(k)(0)
両辺をh(0)n!で割り、fのマクローリン展開によりan=f(n)(0)/n!であることを用いると
an=−∑k=1deg hh(k)(0)/h(0)/k!∗an−k
ここでh(k)(0)/h(0)/k!はnに依らない定数であるから、これは(deg h)+1項間線形漸化式である
多項式で表せるDP
以下の問題を考える:
「正整数の列aが与えられるとき、aの重複なし部分和でちょうどbになるものは何通りあるか?」
この問題は典型的なDPであるが、以下のように多項式によって表せる:
「f=∏i(1+Xai)とするとき、fのb次の係数を求めよ」
この多項式はこの問題に関する母関数であるとも考えられる。
また、同じように、「正整数の列aが与えられるとき、aの重複あり部分和でちょうどbになるものは何通りあるか?」という問題の場合、
f=∏i∑j=0∞Xjaiとなるが、逆元を用いてf=∏i(1−Xai)−1とも表せる。
多次元のDPであっても多変数多項式によって表せ、同じように考えられる。
"戻すDP"
典型的な部分和数え上げ問題のDPの遷移が多項式の掛け算として表せることがわかった。
ならば、その逆演算である割り算がどう適用できるかを考えてみるのは自然だろう。
たとえば、f=∏i(1+Xai)である場合、(1+Xai)で割ることでDPを"戻す"ことができる。
つまり、aから要素aiを削除した列でのDP結果が得られる。
実際には、(1+Xai)−1=∑j=0∞(−X)aijとなる。
単純に再計算する場合O(nb)程度の計算量がかかるが、この場合重複あり部分和DPと同じように実装することでO(b)の計算量で求めることができる。
重複ありの場合、f=∏i(1−Xai)−1であるが、
((1−Xai)−1)−1=1−Xaiというようにそれ自身であるので、"戻す"場合は重複なしDPのような掛け算によって実装できる。
関連する問題
線形漸化式の値
bi=ai(i<k),bi+k=∑j=0k−1cjbi+jと表される線形漸化式を考える。
f=Xk−∑i=0k−1ciXiとし、Xjmodf=∑i=0k−1diXiと表されたとすると、bj=∑i=0k−1aidiとなる。
これを繰り返し二乗法により計算すれば、線形漸化式のj番目の値を、O(k2logj)またはO(M(k)logj)程度の計算量で解くことができる。
関連する問題
多項式補間
f(xi)=yiとなるような長さkの列x,yが与えられているとする。i=jならxi=xjとする。"多項式補間"では、この列から多項式fを復元する。
fmod(X−a)=f(a)である(Polynomial remainder theorem)。
つまり、解きたい式は連立1次合同方程式fmod(X−xi)=yiとなる。これを解くには"中国人剰余定理"が適用できる。
xi=xjならgcd(X−xi,X−xj)=1であることに注意すると、f≡g(mod∏i(X−xi))となるようなgが得られる。
Rが体なら次数k未満の多項式でユニークであるので、復元できたといえる。
中国人剰余定理の実装によってそれぞれ"ラグランジュ補間"や"ニュートン補間"が導出できる。
通常O(n2)程度の計算量で補間できるが、等差数列に対してはほぼ線形時間の計算量で補間できる。
また、高速な多項式乗算のアルゴリズムを使って分割統治法方法を行うとO(M(n)logn)程度の計算量で補間多項式が計算できる。
時々、答えの数列が多項式であることはわかっていても実際の多項式を求めるのは難しかったり計算量がかかってしまうことがある。
このとき、適当な数たちに対して、多項式でなくR上で計算をして、その後に多項式補間をすることで計算量が改善することがある。
関連する問題
彩色多項式
ある無向グラフG=(V,E)に対し、f(c)を、このグラフをc色以下でproper彩色する場合の数、と定義する。
このとき、fは次数∣V∣以下の多項式となっている。
一般のグラフに対して求めるのは難しいが、木やchordal graph, cographなどの特殊なグラフクラスでは効率的に求めることができる。
関連する問題
Polynomial Identity Test
Schwartz–Zippel lemma。
様々な乱択アルゴリズムにおいて重要な役割を果たしている。(加筆求む)
多項式ハッシュ
長さmの文字列Sと適当な関数ordに対し、多項式fS(X)=∑i=0m−1ord(Sm−i)Xiが定義できる。
これを適当な素数pで割った余りを取るとするとfはハッシュ関数の族となる。
ただし、c=dに対してord(c)≡ord(d)(modp)であり、ord(c)≡0(modp)である必要がある。
2つの文字列S,T(∣S∣=m,∣T∣=n)に対して、ランダムなBに対してfS(B)≡fT(B)(modp)となる確率はpmax(m,n)で抑えられる。
このハッシュ関数はハッシュ値のまま様々な操作ができる点で有用である。
例えば、fST=XnfS+fTのようにして文字列連結ができる。
特に文字cに対して、fSc=XfS+ord(c)である。
また、Sの接頭詞S[1,i)に対するハッシュを事前計算しておくことによって、fS[i,j)=fS[1,j)−Xj−ifS[1,i)として部分文字列のハッシュ値を計算できる。
応用は多岐に渡り、文字列検索・LCPの計算など様々なことに利用できる。
多次元にも拡張でき、その場合は多変数多項式になる。
ただし、安易な利用による衝突に関しては、別に記事が必要なほどに落とし穴にはまりやすいので注意すること。
関連する問題