少項式技術

caijianhong發表於2024-11-14

其實就是一些平方暴力的多項式運算,以防某些人在資料範圍允許平方時拍 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;
}

有限微積分相關

有限微積分與數列求和 - 洛谷專欄

相關文章