Berlekamp-Massey 算法

膜拜 ldxoi


用途:

首先我们有一种很快的方法线性递推可以求一个递推式的第 n n n
但是我们不知道这个递推式,只知道这个序列的前面几项
这个时候可以通过 B M BM BM 算法解出最短递推式


算法流程:

对于一个 n n n 项的序列 A = { a 1 , a 2 , . . . , a n } A=\{a_1,a_2,...,a_n\} A={a1,a2,...,an},和一个 m m m 项的序列 R = { r 1 , r 2 , . . . , r n } ( m ≤ n ) R=\{r_1,r_2,...,r_n\}(m\le n) R={r1,r2,...,rn}(mn)
∀ k > m \forall k>m k>m,有 a k = ∑ i = 1 m r i a k − i a_k=\sum_{i=1}^mr_ia_{k-i} ak=i=1mriaki,那么 R R R A A A 的一个递推式
m m m 最小的为最短递推式
现在要通过增量算法求出这个递推式


假设我们处理到当前位置 n n n { a 1 , a 2 , . . . , a n − 1 } \{a_1,a_2,...,a_{n-1}\} {a1,a2,...,an1} 的最短递推式是 { r 1 , r 2 , . . . , r m } \{r_1,r_2,...,r_m\} {r1,r2,...,rm}
记第 i i i 次更改的最短递推式是 R i R_i Ri R 0 = { ∅ } R_0=\{\empty\} R0={}
R c u r R_{cur} Rcur 为当前递推式
现在要让第 n n n 项合法,于是
Δ n = a n − ∑ i = 1 m r i a n − i \Delta n=a_n-\sum_{i=1}^mr_ia_{n-i} Δn=ani=1mriani,分类讨论

  1. Δ n = 0 \Delta n=0 Δn=0,那么 R c u r R_{cur} Rcur 为当前递推式
  2. Δ n ! = 0 \Delta n != 0 Δn!=0,我们需要构造出 R c u r + 1 R_{cur+1} Rcur+1 来满足 n n n

c u r cur cur 分类讨论

  1. c u r = 0 cur=0 cur=0,那么构造一个 R = { r 0 = 0 , r 1 = 0 , . . . , r n = 0 } R=\{r_0=0,r_1=0,...,r_n=0\} R={r0=0,r1=0,...,rn=0}
  2. 考虑构造一个增量递推式 R Δ R_{\Delta} RΔ,满足 R c u r + 1 = R c u r + R Δ R_{cur+1}=R_{cur}+R_{\Delta} Rcur+1=Rcur+RΔ

如何求 R Δ R_{\Delta} RΔ
定义 f a i l i fail_i faili 为第 i i i 个递推式的第一个出错的位置
f a i l c u r = n fail_{cur}=n failcur=n
R Δ R_{\Delta} RΔ m ′ m' m 项,要满足如下条件,在把 n n n 归位时不影响别人

  1. Δ n = ∑ i = 1 m ′ R Δ , i ∗ a n − i \Delta n=\sum_{i=1}^{m'}R_{\Delta,i}*a_{n-i} Δn=i=1mRΔ,iani
  2. ∀ m ′ < k < n , ∑ i = 1 m ′ R Δ , i a k − i = 0 \forall m'<k<n,\sum_{i=1}^{m'}R_{\Delta,i}a_{k-i}=0 m<k<n,i=1mRΔ,iaki=0

考虑到上一层的 R c u r − 1 R_{cur-1} Rcur1 R Δ R_{\Delta} RΔ长得真像!

  1. Δ f a i l c u r − 1 = a f a i l c u r − 1 − ∑ i = 1 m c u r − 1 R c u r − 1 , i ∗ a f a i l c u r − 1 − i \Delta fail_{cur-1}=a_{fail_{cur-1}}-\sum_{i=1}^{m_{cur-1}}R_{cur-1,i}*a_{fail_{cur-1}-i} Δfailcur1=afailcur1i=1mcur1Rcur1,iafailcur1i
  2. ∀ m c u r − 1 < k < f a i l c u r − 1 , a k − ∑ i = 1 m c u r − 1 R c u r − 1 , i a k − i = 0 \forall m_{cur-1}<k<fail_{cur-1},a_k-\sum_{i=1}^{m_{cur-1}}R_{cur-1,i}a_{k-i}=0 mcur1<k<failcur1,aki=1mcur1Rcur1,iaki=0

t = Δ n Δ f a i l c u r − 1 , b = { t , − t ∗ R c u r − 1 , 1 , − t ∗ R c u r − 1 , 2 , . . . , − t ∗ R c u r − 1 , m c u r − 1 } t=\frac{\Delta n}{\Delta fail_{cur-1}},b=\{t,-t*R_{cur-1,1},-t*R_{cur-1,2},...,-t*R_{cur-1,m_{cur-1}}\} t=Δfailcur1Δn,b={t,tRcur1,1,tRcur1,2,...,tRcur1,mcur1}

然后就可以按如下方式构造 R Δ R_{\Delta} RΔ

  1. ∀ f a i l c u r − 1 < k < n \forall fail_{cur-1}<k<n failcur1<k<n,给 a n − k a_{n-k} ank 分配 0 的系数
  2. ∀ f a i l c u r − 1 − m c u r − 1 ≤ k ≤ f a i l c u r − 1 \forall fail_{cur-1}-m_{cur-1}\le k\le fail_{cur-1} failcur1mcur1kfailcur1,给 a n − k a_{n-k} ank 分配 b f a i l c u r − 1 − k + 1 b_{fail_{cur-1}-k+1} bfailcur1k+1 的系数

这样构造出来的 R Δ = { 0 , 0 , . . . , 0 , t , − t ∗ r c u r − 1 , 1 , − t ∗ r c u r − 1 , 2 , . . . , − t ∗ r c u r − 1 , m c u r − 1 } R_{\Delta}=\{0,0,...,0,t,-t*r_{cur-1,1},-t*r_{cur-1,2},...,-t*r_{cur-1,m_{cur-1}}\} RΔ={0,0,...,0,t,trcur1,1,trcur1,2,...,trcur1,mcur1}
满足条件


理解:
Δ f a i l c u r − 1 = a f a i l c u r − 1 − ∑ i = 1 m c u r − 1 R c u r − 1 , i ∗ a f a i l c u r − 1 − i \Delta fail_{cur-1}=a_{fail_{cur-1}}-\sum_{i=1}^{m_{cur-1}}R_{cur-1,i}*a_{fail_{cur-1}-i} Δfailcur1=afailcur1i=1mcur1Rcur1,iafailcur1i 乘上 t t t
Δ n = a f a i l c u r − 1 ∗ t − ∑ i = 1 m c u r − 1 a f a i l c u r − 1 − i ∗ ( t ∗ R c u r − 1 , i ) \Delta n=a_{fail_{cur-1}}*t-\sum_{i=1}^{m_{cur-1}}a_{fail_{cur-1}-i}*(t*R_{cur-1,i}) Δn=afailcur1ti=1mcur1afailcur1i(tRcur1,i)
然后用 { a f a i l c u r − 1 − m c u r − 1 , . . . , a f a i l c u r − 1 } \{a_{fail_{cur-1}}-m_{cur-1},...,a_{fail_{cur-1}}\} {afailcur1mcur1,...,afailcur1} 去构造 n n n
然后用 0 0 0 去满足 [ f a i l c u r − 1 , n − 1 ] [fail_{cur-1},n-1] [failcur1n1] 不变


u p t : 19 / 12 / 02 upt:19/12/02 upt:19/12/02
新的递推式 Δ R \Delta R ΔR的项数是 n − f a i l [ b e s t ] + l e n [ b e s t ] n-fail[best]+len[best] nfail[best]+len[best],因为要求最短递推式,所以还要动态维护一个 b e s t best best


模板

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
const int Mod = 998244353, G = 3;
cs int N = 5e5 + 5;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1){if(b&1) ans = mul(ans, a); a = mul(a, a);} return ans;}
void Add(int &a, int b){ a = add(a, b); }
int dec(int a, int b){return a - b < 0 ? a - b + Mod : a - b; }
ll inv[N];
#define poly vector<ll>
#define C 20
poly w[C + 1];
int up, bit, rev[N]; 
void Init(int len){ up = 1, bit = 0;	
	while(up < len) up <<= 1, bit++;
	for(int i = 0; i < up; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void prework(){
	inv[0] = inv[1] = 1;
	for(int i = 2; i <= N-5; i++) inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
	for(int i = 1; i <= C; i++) w[i].resize(1<<(i-1));
	ll wn = ksm(G, (Mod-1)/(1<<C)); w[C][0] = 1; 
	for(int i = 1; i < (1<<(C-1)); i++) w[C][i] = mul(w[C][i-1], wn);
	for(int i = C-1; i; i--)
		for(int j = 0; j < (1<<(i-1)); j++)
			w[i][j] = w[i+1][j<<1];
}
void NTT(poly &a, int flag){
	for(int i = 0; i < up; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int i = 1, l = 1; i < up; i <<= 1, l++)
		for(int j = 0; j < up; j += (i<<1))
			for(int k = 0; k < i; k++){
				ll x = a[k + j], y = mul(w[l][k], a[k + j + i]);
				a[k + j] = add(x, y); a[k + j + i] = add(x, Mod-y);
			}
	if(flag == -1){
		reverse(a.begin() + 1, a.begin() + up);
		for(int i = 0; i < up; i++) a[i] = mul(a[i], inv[up]);
	}
}
poly operator * (poly a, poly b){
	int len = a.size() + b.size() - 1;
	Init(len); a.resize(up); b.resize(up);
	NTT(a, 1); NTT(b, 1);
	for(int i = 0; i < up; i++) a[i] = mul(a[i], b[i]); 
	NTT(a, -1); a.resize(len); return a;
}
poly Inv(poly a, int len){
	poly b(1, ksm(a[0], Mod - 2)), c;
	for(int lim = 4; lim < (len << 2); lim <<= 1){
		Init(lim); c = a; c.resize(lim >> 1);
		c.resize(up); b.resize(up); NTT(c, 1); NTT(b, 1);
		for(int i = 0; i < up; i++) b[i] = mul(b[i], add(2, Mod - mul(b[i], c[i])));
		NTT(b, -1); b.resize(lim >> 1);
	} b.resize(len); return b;
}
poly operator - (poly a, poly b){
	poly c; int len = max(a.size(), b.size()); c.resize(len);
	a.resize(len); b.resize(len);
	for(int i = 0; i < len; i++) c[i] = add(a[i], Mod - b[i]);
	return c;
}
poly operator + (poly a, poly b){
	poly c; int len = max(a.size(), b.size()); c.resize(len);
	a.resize(len); b.resize(len);
	for(int i = 0; i < len; i++) c[i] = add(a[i], b[i]);
	return c;
}
poly operator / (poly a, poly b){
	int lim = 1, len = a.size() - b.size() + 1;
	reverse(a.begin(), a.end()); reverse(b.begin(), b.end());
	while(lim <= len) lim <<= 1;
	b = Inv(b, lim); b.resize(len);
	a = a * b; a.resize(len);
	reverse(a.begin(), a.end()); 
	return a;
}
poly operator % (poly a, poly b){ 
	int len = a.size() - b.size() + 1;
	if(len < 0) return a;
	poly c = a - (a / b) * b;
	c.resize(b.size() - 1); return c;
}
int calc(poly coe, int *a, int k, int n){
	poly p(k + 1), f(2), g(1); f[1] = 1; g[0] = 1; p[k] = 1; 
	for(int i = 1; i <= k; i++) p[k - i] = coe[i] ? Mod - coe[i] : 0;
	for(;n;n>>=1, f=f*f%p) if(n&1) g = g*f % p;
	int ans = 0;
	for(int i = 0; i < k; i++) ans = add(ans, mul(a[i], g[i]));
	return ans;
}
int n, m, a[N];
namespace BM{
	poly best, cur; int fail = 0, val = 0;
	void upt(int n){
		int t = a[n];
		for(int i = 1; i < cur.size(); i++) Add(t, Mod-mul(cur[i], a[n - i]));
		if(!t) return;
		if(cur.empty()){ cur.resize(n + 1); fail = n; val = t; return; }
		int coef = mul(t, ksm(val, Mod-2));
		poly nxt(n - fail);
		nxt.push_back(coef);
		for(int i = 1; i < best.size(); i++) nxt.push_back(mul(coef, dec(0, best[i])));
		nxt = nxt + cur;
		if((int)cur.size() - n <= (int)best.size() - fail){
			best = cur; fail = n; val = t;
		} cur = nxt;
	}
	int solve(){
		for(int i = 1, up = cur.size() - 1; i <= up; i++) cout << cur[i] << " ";
		puts("");
		return calc(cur, a + 1, cur.size() - 1, m);
	}
}
int main(){
	n = read(), m = read(); prework();
	for(int i = 1; i <= n; i++) a[i] = read(), BM::upt(i);
	cout << BM::solve(); return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值