其實就是一些平方暴力的多項式運算,以防某些人在資料範圍允許平方時拍 NTT 上去。剛好出題用到了少項式技術就象徵地總結一下。
普通冪少項式
單點求值
struct poly : vector<mint> {
using vector::vector;
mint operator()(const mint& x) const {
auto&& f = *this;
mint res = 0, now = 1;
for (int i = 0; i < (int)f.size(); i++) res += now * f[i], now *= x - i;
return res;
}
};
整除與取模
將被除式的每一個高位,嘗試用除式的某一倍數去減,使其消失。
poly operator/(poly lhs, const poly& rhs) {
if (lhs.size() < rhs.size()) return {};
mint iz = 1 / rhs.back();
vector<mint> ret(lhs.size() - rhs.size() + 1);
for (int i = (int)lhs.size() - 1; i >= (int)rhs.size() - 1; i--) {
auto k = ret[i - rhs.size() + 1] = lhs[i] * iz;
for (int j = 0; j < (int)rhs.size(); j++) lhs[i - j] -= rhs[rhs.size() - j - 1] * k;
}
return ret;
}
拉格朗日插值
根據不知道哪裡來的實踐經驗,拉插特有的除二項式部分需要特別最佳化。
poly divide2(poly lhs, mint b) {
vector<mint> ret(lhs.size() - 1);
for (int i = (int)lhs.size() - 1; i >= 1; i--) {
auto k = ret[i - 1] = lhs[i];
lhs[i - 1] -= b * k;
}
return ret;
}
poly lagrange(const vector<pair<mint, mint>>& vec) {
poly ans(vec.size()), prod{1};
for (int i = 0; i < (int)vec.size(); i++) prod = prod * poly{0 - vec[i].first, 1};
for (int i = 0; i < (int)vec.size(); i++) {
mint den = 1;
for (int j = 0; j < (int)vec.size(); j++) {
if (i != j) den *= vec[i].first - vec[j].first;
}
auto num = divide2(prod, 0 - vec[i].first);
auto k = vec[i].second / den;
for (int j = 0; j < (int)vec.size(); j++) ans[j] += num[j] * k;
}
return ans;
}
對數與指數
【模板】子集卷積 - caijianhong - 部落格園
根據求導公式比較兩邊係數。
下降冪少項式
單點求值
struct poly : vector<mint> {
using vector::vector;
mint operator()(const mint& x) const {
auto&& f = *this;
mint res = 0, now = 1;
for (int i = 0; i < (int)f.size(); i++) res += now * f[i], now *= x - i;
return res;
}
};
普通冪轉下降冪
使用第二類斯特林數。
從階乘冪到斯特林數 - caijianhong - 部落格園
poly nor2under(const poly& f) {
poly g(f.size());
for (int i = 0; i < (int)f.size(); i++) {
for (int j = 0; j <= i; j++) g[j] += S[i][j] * f[i];
}
return g;
}
S[0][0] = 1;
for (int i = 1; i <= 2000; i++) {
for (int j = 1; j <= i; j++) S[i][j] = S[i - 1][j] * j + S[i - 1][j - 1];
}
下降冪轉普通冪
使用第一類斯特林數並攜帶某個係數。
poly under2nor(const poly& f) {
poly g(f.size());
for (int i = 0; i < (int)f.size(); i++) {
for (int j = 0; j <= i; j++) g[j] += S1[i][j] * ((i - j) & 1 ? -1 : +1) * f[i];
}
return g;
}
S1[0][0] = 1;
for (int i = 1; i <= 2000; i++) {
for (int j = 1; j <= i; j++) S1[i][j] = S1[i - 1][j] * (i - 1) + S1[i - 1][j - 1];
}
點值平移
\(f(x)\to f(x-b)\)。觀察到下降冪也有二項式定理。
poly translate(const poly& f, int b) {
poly g(f.size());
for (int i = 0; i < (int)f.size(); i++) {
mint now = 1;
for (int j = 0; j <= i; j++) {
g[i - j] += f[i] * binom(i, j) * now;
now *= -b - j;
}
}
return g;
}
有限微積分相關
有限微積分與數列求和 - 洛谷專欄