Berlekamp–Massey 算法求最短递推式 & Bostan-Mori 算法数列远端求值 / ARIS0_0 - 20
本文将介绍 Berlekamp-Massey 算法求最短递推式和 Bostan-Mori 算法数列远端求值。
Berlekamp-Massey 算法
给长为
考虑增量构造:按照
假设我们考虑到
令
如果 i 是第一个需要对递推系数进行修改的位置
说明
如果 i 不是第一个需要对递推系数进行修改的位置
设上一个需要对递推系数进行修改的位置为
考虑构造一个序列
设在考虑完
验证此构造是否对于
由于
再验证此构造是否在
也成立。
令
一个实现细节:如果
常系数齐次线性递推数列表示为有理函数
给定序列
考虑
所以,构造
Bostan-Mori 算法
给多项式
考虑把
这时,分母是偶函数,可以表示为
考虑把分子奇偶项拆开,表示为
这样,每次
Bostan-Mori 数列远端求值
给定序列
把
#include <cstdio>
#include <vector>
#define bit_ceil(x) ((x) <= 1 ? 1 : (1 << (32 - __builtin_clz((x) - 1))))
typedef std::vector<int> poly;
namespace FastIn
{
const int S = 4e6 + 7;
char *p1, *p2, buf[S];
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, S, stdin), p1 == p2) ? EOF : *p1++)
inline void read(int &x)
{
x = 0;
char c = gc();
while (c < '0' || c > '9')
c = gc();
for (; c >= '0' && c <= '9'; c = gc())
x = (x << 3) + (x << 1) + (c & 15);
}
#undef gc
}
using FastIn::read;
const int mod = 998244353;
inline int M(const int &x) { return x >= mod ? x - mod : x; }
inline int qpow(int a, int b)
{
int res = 1;
for (; b; a = (long long)(a) * a % mod, b >>= 1)
(b & 1) && (res = (long long)(res) * a % mod);
return res;
}
inline void NTT(poly &a, const int &rvs)
{
static std::vector<int> rev;
int n = a.size(); (int)(rev.size()) < n && (rev.resize(n), true);
for (int i = 0; i < n; ++i)
if (i < (rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (n >> 1))))
std::swap(a[i], a[rev[i]]);
for (int len = 1, W; len < n; len <<= 1)
{
W = qpow(rvs == 1 ? 3 : 332748118, (mod - 1) / (len << 1));
for (int i = 0; i < n; i += (len << 1))
for (int j = i, f0, f1, w = 1; j < i + len; ++j, w = (long long)(w) * W % mod)
f0 = a[j], f1 = (long long)(w) * a[j + len] % mod, a[j] = M(f0 + f1), a[j + len] = M(f0 + mod - f1);
}
if (rvs == -1)
for (int i = 0, invn = qpow(n, mod - 2); i < n; ++i)
a[i] = (long long)(a[i]) * invn % mod;
}
inline poly operator*(const poly &A, const poly &B)
{
int n = A.size() + B.size() - 1, len = bit_ceil(n);
poly a = A, b = B;
a.resize(len), b.resize(len), NTT(a, 1), NTT(b, 1);
for (int i = 0; i < len; ++i)
a[i] = (long long)(a[i]) * b[i] % mod;
return NTT(a, -1), a.resize(n), a;
}
inline poly operator*=(poly &a, const poly &b) { return a = a * b; }
inline poly operator+(const poly &A, const poly &B)
{
poly a = A, b = B;
a.resize(std::max(A.size(), B.size())), b.resize(std::max(A.size(), B.size()));
for (int i = 0; i < (int)(a.size()); ++i)
a[i] = M(a[i] + b[i]);
return a;
}
namespace BM
{
int n, a[10007], d[10007], p[10007];
poly f[10007];
inline void solve()
{
(d[0] = a[0]) ? (f[0] = (poly){0}, p[0] = 0) : (p[0] = -1);
for (int i = 1, k; i < n; ++i)
{
if (~p[i - 1])
for (int j = 0; j < (int)(f[p[i - 1]].size()); ++j)
d[i] = (d[i] + (long long)(f[p[i - 1]][j]) * a[i - j - 1]) % mod;
if (!(d[i] = M(a[i] + mod - d[i])))
{
p[i] = p[i - 1];
continue;
}
else if (p[i - 1] == -1)
{
f[p[i] = i].resize(i + 1);
continue;
}
int c = (long long)(d[i]) * qpow(d[k = p[i - 1]], mod - 2) % mod;
poly g;
for (int j = 0; j < i - k - 1; ++j)
g.push_back(0);
g.push_back(c);
if ((~(k - 1)) && (~(p[k - 1])))
for (const int &j : f[p[k - 1]])
g.push_back((long long)(mod - c) * j % mod);
poly tmp = f[k] + g;
if (f[k].size() == tmp.size())
f[p[i] = k] = tmp;
else
f[p[i] = i] = tmp;
}
}
}
inline int BostanMori(poly p, poly q, int k)
{
while (k)
{
int n = p.size(), m = q.size();
poly tmp = q;
for (int i = 1; i < m; i += 2)
tmp[i] = M(mod - q[i]);
p *= tmp, q *= tmp, n = (p.size() + ((k & 1) ^ 1)) >> 1;
for (int i = 0; i < n; ++i)
p[i] = p[(i << 1) + (k & 1)];
for (int i = 0; i < m; ++i)
q[i] = q[i << 1];
p.resize(n), q.resize(m), k >>= 1;
}
return (long long)(p[0]) * qpow(q[0], mod - 2) % mod;
}
int n, m, ans, a[10007];
poly f;
int main()
{
read(n), read(m);
for (int i = 0; i < n; ++i)
read(a[i]), BM::a[i] = a[i];
BM::n = n, BM::solve();
if (BM::p[BM::n - 1] == -1)
return putchar('0'), putchar('\n'), putchar('0'), 0;
poly p, q = (poly){1};
for (int i = 0; i < (int)(BM::f[BM::p[BM::n - 1]].size()); ++i)
p.push_back(a[i]);
for (const int &i : BM::f[BM::p[BM::n - 1]])
q.push_back(M(mod - i));
p *= q, p.resize(BM::f[BM::p[BM::n - 1]].size());
for (const int &i : BM::f[BM::p[BM::n - 1]])
printf("%d ", i);
printf("\n%d", BostanMori(p, q, m));
return 0;
}
// 不曾走过炫暇盛宴的绝顶,唯能步入终幕遗信的黄昏
// 20