This documentation is automatically generated by online-judge-tools/verification-helper
#include "formal_power_series/sum_of_exponential_times_polynomial.hpp"
一般に $f(x)$ を高々 $d$ 次の多項式として,$\displaystyle \sum_{i=0}^{N - 1} r^i f(i)$ を求める.先頭 $d + 1$ 項の値が既知であれば計算量は $O(d)$.関数は分かるが具体的な値が未知の場合は $O\left(d \left(\log d\right)^2\right)$ で先頭 $d + 1$ 項の値の multipoint evaluation を行う必要があると思われる.
min_25 さん によると,求めたい値は $C + g(x) r^x$, $g(x)$ はなんらかの多項式,という形になる.したがって,$r = 1$ の場合はそのまま Lagrange 補間を行えばよく,$r \neq 1$ の場合は $C$ や $r^x$ の分を打ち消した $g(x)$ に対して Lagrange 補間を行えばよい.$C$ の値は sum_of_sum_of_exponential_times_polynomial_limit()
関数によって $O(d)$ で求められる.
先頭の $d + 1$ 項を計算して sum_of_exponential_times_polynomial()
関数に与える.
using mint = ModInt<998244353>;
vector<mint> xs(d + 1);
for (int i = 0; i <= d; i++) xs[i] = i;
vector<mint> f = some_function();
vector<mint> initial_terms = MultipointEvaluation<mint>(xs).evaluate_polynomial(f);
mint sum = sum_of_exponential_times_polynomial<mint>(r, initial_terms, 12345678910111213LL);
単項式の場合,先頭 $d + 1$ 項の列挙が線形篩により $O(d)$ で可能である.使用方法は以下の通り.
mint r;
int d;
vector<mint> initial_terms = Sieve(d).enumerate_kth_pows<mint>(d, d);
cout << sum_of_exponential_times_polynomial<mint>(r, initial_terms, 12345678910111213LL) << '\n';
#pragma once
#include "lagrange_interpolation.hpp"
#include "sum_of_exponential_times_polynomial_limit.hpp"
#include <cassert>
#include <vector>
// CUT begin
// $d$ 次以下の多項式 $f(x)$ と定数 $r$ について,
// $\sum_{i=0}^{N-1} r^i f(i)$ の値を $[f(0), ..., f(d - 1), f(d)]$ の値から $O(d)$ で計算.
// Reference:
// - https://judge.yosupo.jp/problem/sum_of_exponential_times_polynomial
// - https://min-25.hatenablog.com/entry/2015/04/24/031413
template <typename MODINT>
MODINT sum_of_exponential_times_polynomial(MODINT r, const std::vector<MODINT> &init, long long N) {
if (N <= 0) return 0;
if (init.size() == 1) return init[0] * (r == 1 ? MODINT(N) : (1 - r.pow(N)) / (1 - r));
std::vector<MODINT> S(init.size() + 1);
MODINT rp = 1;
for (int i = 0; i < int(init.size()); i++) {
S[i + 1] = S[i] + init[i] * rp;
rp *= r;
}
if (N < (long long)S.size()) return S[N];
if (r == 1) return interpolate_iota<MODINT>(S, N);
const MODINT Sinf = sum_of_exponential_times_polynomial_limit<MODINT>(r, init);
MODINT rinv = r.inv(), rinvp = 1;
for (int i = 0; i < int(S.size()); i++) {
S[i] = (S[i] - Sinf) * rinvp;
rinvp *= rinv;
}
return interpolate_iota<MODINT>(S, N) * r.pow(N) + Sinf;
};
#line 2 "formal_power_series/lagrange_interpolation.hpp"
#include <vector>
// CUT begin
// Lagrange interpolation
// Input: [f(0), ..., f(N-1)] (length = N), deg(f) < N
// Output: f(x_eval)
// Complexity: O(N)
// Verified: https://atcoder.jp/contests/arc033/tasks/arc033_4
template <typename MODINT> MODINT interpolate_iota(const std::vector<MODINT> ys, MODINT x_eval) {
const int N = ys.size();
if (x_eval.val() < N) return ys[x_eval.val()];
std::vector<MODINT> facinv(N);
facinv[N - 1] = MODINT(N - 1).fac().inv();
for (int i = N - 1; i > 0; i--) facinv[i - 1] = facinv[i] * i;
std::vector<MODINT> numleft(N);
MODINT numtmp = 1;
for (int i = 0; i < N; i++) {
numleft[i] = numtmp;
numtmp *= x_eval - i;
}
numtmp = 1;
MODINT ret = 0;
for (int i = N - 1; i >= 0; i--) {
MODINT tmp = ys[i] * numleft[i] * numtmp * facinv[i] * facinv[N - 1 - i];
ret += ((N - 1 - i) & 1) ? (-tmp) : tmp;
numtmp *= x_eval - i;
}
return ret;
}
#line 2 "formal_power_series/sum_of_exponential_times_polynomial_limit.hpp"
#include <cassert>
#line 4 "formal_power_series/sum_of_exponential_times_polynomial_limit.hpp"
// CUT begin
// $d$ 次以下の多項式 $f(x)$ と定数 $r$ について,
// $\sum_{i=0}^\infty r^i f(i)$ の値を $[f(0), ..., f(d - 1), f(d)]$ の値から $O(d)$ で計算.
// Requirement: r != 1
// https://judge.yosupo.jp/problem/sum_of_exponential_times_polynomial_limit
// Document: https://hitonanode.github.io/cplib-cpp/formal_power_series/sum_of_exponential_times_polynomial_limit.hpp
template <typename MODINT>
MODINT sum_of_exponential_times_polynomial_limit(MODINT r, std::vector<MODINT> init) {
assert(r != 1);
if (init.empty()) return 0;
if (init.size() == 1) return init[0] / (1 - r);
auto &bs = init;
const int d = int(bs.size()) - 1;
MODINT rp = 1;
for (int i = 1; i <= d; i++) rp *= r, bs[i] = bs[i] * rp + bs[i - 1];
MODINT ret = 0;
rp = 1;
for (int i = 0; i <= d; i++) {
ret += bs[d - i] * MODINT(d + 1).nCr(i) * rp;
rp *= -r;
}
return ret / MODINT(1 - r).pow(d + 1);
};
#line 6 "formal_power_series/sum_of_exponential_times_polynomial.hpp"
// CUT begin
// $d$ 次以下の多項式 $f(x)$ と定数 $r$ について,
// $\sum_{i=0}^{N-1} r^i f(i)$ の値を $[f(0), ..., f(d - 1), f(d)]$ の値から $O(d)$ で計算.
// Reference:
// - https://judge.yosupo.jp/problem/sum_of_exponential_times_polynomial
// - https://min-25.hatenablog.com/entry/2015/04/24/031413
template <typename MODINT>
MODINT sum_of_exponential_times_polynomial(MODINT r, const std::vector<MODINT> &init, long long N) {
if (N <= 0) return 0;
if (init.size() == 1) return init[0] * (r == 1 ? MODINT(N) : (1 - r.pow(N)) / (1 - r));
std::vector<MODINT> S(init.size() + 1);
MODINT rp = 1;
for (int i = 0; i < int(init.size()); i++) {
S[i + 1] = S[i] + init[i] * rp;
rp *= r;
}
if (N < (long long)S.size()) return S[N];
if (r == 1) return interpolate_iota<MODINT>(S, N);
const MODINT Sinf = sum_of_exponential_times_polynomial_limit<MODINT>(r, init);
MODINT rinv = r.inv(), rinvp = 1;
for (int i = 0; i < int(S.size()); i++) {
S[i] = (S[i] - Sinf) * rinvp;
rinvp *= rinv;
}
return interpolate_iota<MODINT>(S, N) * r.pow(N) + Sinf;
};