1443 字
7 分钟
Kitamasa 与 BM 算法学习笔记
NOTE下文的序列均为 -index
Kitamasa 算法
序列 满足 ,求
显然序列中任意项都能满足存在 使得 ,令
, ,考虑如何计算
, 仅有 种,尝试递推
考虑多项式形式,
( 即初始给定的递推数列),位移再叠加即可,复杂度
现在我们有了 ,那么就能使用快速幂 完成 的实现
但是我们还需要单位元 和 基础底数
- 单位元,即
- 基础底数,即
31 collapsed lines
ll kitamasa(const vector<ll>& coef, const vector<ll>& a, ll n) { if(n < (int)a.size()) return a[n];
int k = coef.size(); assert(k != 0);
auto compose = [&](const vector<ll>& A, const vector<ll>& _B) -> vector<ll> { vector<ll> B = _B; vector<ll> C(k); for(auto v: A) { for(int j = 0; j < k; j++) (C[j] += v * B[j] % mod) %= mod;
ll back = B.back(); for(int j = k - 1; j >= 1; j--) B[j] = (B[j - 1] + back * coef[j] % mod) % mod; B[0] = back * coef[0] % mod; }
return C; };
vector<ll> res_c(k), c(k); res_c[0] = 1, c[1] = 1; while(n) { if(n & 1ll) res_c = compose(res_c, c); n >>= 1, c = compose(c, c); } ll res = 0; for(int j = 0; j < k; j++) (res += res_c[j] * a[j] % mod) %= mod; return res;}例题
T - フィボナッチ
= + … +
を mod 1,000,000,007 で求めよ。
Berlekamp-Massey 算法
给定一个数列 ,求满足 的最短递推关系(即阶数 最小)
延用但略有不同于 kitamasa 中的记号: 使得
初始时
考虑按顺序递推,若 也适用于 ,则 ,否则令
递推过程中不可能只存在于一个错误,否则数列类似于 ,无正确递推关系
对于数列中的第一个错误,显然无论如何调整都无法使得 存在,令此时 为 个 方便后续使用( -index )
设某次的位置为 ,误差为 ,那么有
当前位置:
设
那么新 的长度为
显然 有 单调且斜率小于等于 , 单调且斜率为 ,故 单调不增,每次仅记录上一次的 错误点 即可满足 阶数最小
128 collapsed lines
#include <bits/stdc++.h>#include <cassert>#define print(vec) print_v(#vec, vec)#define debug(x) cout << #x << ": " << x << endlusing namespace std;using ll = long long;const ll mod = 998244353;ll qpow(ll x, ll k) { ll res = 1; while(k) { if(k & 1ll) res = res * x % mod; x = x * x % mod, k >>= 1; } return res;}ll Inv(ll x) { return qpow(x, mod - 2);}
template<typename T>void print_v(string name, const vector<T>& vec) { cout << name << ": " << endl; for(auto v: vec) cout << v << ' '; cout << endl;}
ll kitamasa(const vector<ll>& coef, const vector<ll>& a, ll n) { if(n < (int)a.size()) return a[n];
int k = coef.size(); assert(k != 0);
auto compose = [&](const vector<ll>& A, const vector<ll>& _B) -> vector<ll> {
vector<ll> B = _B; vector<ll> C(k); for(auto v: A) { for(int j = 0; j < k; j++) (C[j] += v * B[j] % mod) %= mod;
ll back = B.back(); for(int j = k - 1; j >= 1; j--) B[j] = (B[j - 1] + back * coef[j] % mod) % mod; B[0] = back * coef[0] % mod; }
return C; };
vector<ll> res_c(k), c(k); res_c[0] = 1, c[1] = 1; while(n) { if(n & 1ll) res_c = compose(res_c, c); n >>= 1, c = compose(c, c); } ll res = 0; for(int j = 0; j < k; j++) (res += res_c[j] * a[j] % mod) %= mod; return res;}
vector<ll> BM(const vector<ll>& a) { int preI = -1; ll preD = 0; vector<ll> coef, pre_c, tmp; for(int i = 0; i < (int)a.size(); i++) { ll D = a[i]; for(int j = 0; j < (int)coef.size(); j++) (D -= coef[j] * a[i - j - 1] % mod) %= mod; D = (D + mod) % mod;
if(D == 0) continue;
if(preI < 0) { coef = vector(i + 1, 0ll); preI = i; preD = D; continue; }
int bias = i - preI; int oldlen = coef.size(); int newlen = bias + pre_c.size(); if(newlen > oldlen) { tmp = coef; coef.resize(newlen); }
ll Delta = D * Inv(preD) % mod; (coef[bias - 1] += Delta) %= mod; for(int j = 0; j < (int)pre_c.size(); j++) { (coef[bias + j] -= Delta * pre_c[j] % mod) %= mod; }
if(newlen > oldlen) { pre_c = std::move(tmp); preI = i; preD = D; } } return coef;}
ll guessNth(const vector<ll>& a, ll n) { vector<ll> coef = BM(a);
for(auto v: coef) cout << (v + mod) % mod << ' '; cout << endl;
ranges::reverse(coef); ll res = kitamasa(coef, a, n); return (res + mod) % mod;}
void solve() { int n, m; cin >> n >> m; vector<ll> a(n); for(auto& v: a) cin >> v; cout << guessNth(a, m) << endl;}int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); // freopen("A.in", "r", stdin); // freopen("A.out", "w", stdout); int T = 1; // cin >> T; while(T--) solve();}关于插入上一次错误的实现细节:
相对于 的位置 实际为 (因为 -index)
的系数简单从 开始就行了
例题
P5487 【模板】Berlekamp–Massey 算法
参考资料
Kitamasa 与 BM 算法学习笔记
https://dk-qwq.github.io/blog/posts/kitamasa_bm_notes/