ラグランジュ補間
次の問題を解くアルゴリズム
次以下の多項式 が存在する. となる の組が 個存在するときに を復元しろ.
求める の形式で場合分けする.
を一つ求める (はgiven)
とすると (ただしは未知の係数) と書ける.このように を定めると となるのが都合がいい. となるため と係数 を求められる.
は で求められるので で を求められる.
は で求められるので は で求められる.
// x座標が相異なるn+1点(x_i,y_i)を通るn次以下の多項式f(T)の値を返す // O(n^2) mint lagrange_interpolation(vector<mint> x, vector<mint> y, mint T) { const ll n = x.size() - 1; mint ret = 0; REP(i, n+1) { mint t = 1; REP(j, n+1) { if(i == j) continue; t *= T-x[j]; t /= x[i]-x[j]; } ret += t * y[i]; } return ret; }
ARC033 D - 見たことのない多項式 の部分点 「提出」
の係数を求める
を求めるところまでは同じようにやる.
を求めるために を で求める.これを戻すDPを使って行う. の の係数をDPを使って計算する.まで見たときのの係数 とする.遷移は となる.このDPの計算は でできる.
から を復元することを考える. が小さい方から順番に見ていく. であるから と求められる. であるから と求められる.このようにdpを戻す操作は を で割る操作に対応する.
よって を で求めることができたので を で求められた.
// x座標が相異なるn+1点(x_i,y_i)を通るn次以下の多項式f(x)を返す // O(n^2) vector<mint> lagrange_interpolation(vector<mint> x, vector<mint> y) { const ll n = x.size() - 1; REP(i, n+1) { mint t = 1; REP(j, n+1) if(i != j) t *= x[i]-x[j]; y[i] /= t; } ll cur = 0, nxt = 1; vector<vector<mint>> dp(2, vector<mint>(n+2)); dp[0][0] = -1 * x[0], dp[0][1] = 1; FOR(i, 1, n+1) { REP(j, n+2) { dp[nxt][j] = dp[cur][j] * -1 * x[i]; if(j >= 1) dp[nxt][j] += dp[cur][j-1]; } swap(nxt, cur); } vector<mint> xinv(n+1); REP(i, n+1) xinv[i] = x[i].inv(); vector<mint> ret(n+1); // f(x) REP(i, n+1) { if(y[i]==0) continue; // 0割り対策の場合分け if(x[i] == 0) { REP(j, n+1) ret[j] += dp[cur][j+1] * y[i]; } else { ret[0] -= dp[cur][0] * xinv[i] * y[i]; mint pre = -1 * dp[cur][0] * xinv[i]; FOR(j, 1, n+1) { ret[j] -= (dp[cur][j] - pre) * xinv[i] * y[i]; pre = -1 * (dp[cur][j] - pre) * xinv[i]; } } } return ret; }
ABC137 F - Polynomial Construction 「提出」
与えられる点の座標が等差数列
が等差数列になってるケースではもっと高速に解ける.
初項で交差の等差数列とする.
を一つ求める (Tはgiven)
求めるのに かけていた を高速に求めることができればうれしい.
と の形が似ていることを使う.
比較すると が増えて が消えている.したがって で から を求められる.
を計算するには,予め を計算しておいて, で割ることで高速に計算できる.
modの割り算が必須なので計算量は全体で となる.
// x座標が相異なるn+1点(x_i,y_i)を通るn次以下の多項式f(T)の値を返す // x_i = a + i*d 0<=i<=n (等差数列) // O(nlog(MOD)) mint lagrange_interpolation_arithmetic(mint a, mint d, vector<mint> y, mint T) { const ll n = y.size() - 1; mint ret = 0, ft = 1; REP(i, n+1) ft *= T-(a+d*i); // f_0(x_0) mint f = 1; FOR(i, 1, n+1) f *= -1*i*d; ret += c[0] * ft / (T-a); // f_i(x_i) → f_{i+1}(x_{i+1}) REP(i, n) { f *= d*(i+1) / (d*(i-n)); ret += y[i+1] / f * ft / (T-a-d*(i+1)); } return ret; }
例題
ネタバレ注意
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
codeforces Educational Codeforces Round 7 F. The Sum of the k-th Powers
問題ページ
とすると答えは になる. はk+1次式っぽいので のk+2点を使ってラグランジュ補間をすれば が求まる.
答えが多項式の値なことはわかっているが具体的に多項式を構成するのがつらいときに使う
Submission #58554396 - Codeforces
yukicoder No.42 貯金箱の溜息
問題ページ
dp[i番目のコインまで][金額j] = (支払う方法の数) としたdpテーブルでf(m) = dp[5][500m+q] q<500 はmについて5次式になっている(らしい).f(0)=dp[5][500×0+q], f(1)=dp[5][500×1+q], … , f(5)=dp[5][500×5+q] の6点を用いて多項式fを復元し,f(M/500)を求める.
答えが多項式の値なことはわかっているが具体的に多項式を構成するのがつらいときに使う
(これが多項式だって気づく難易度高すぎない?)
提出 #367653 No.42 貯金箱の溜息 - yukicoder
ライブラリ作ったはいいけど今後使うことはあるのだろうか