BM算法學習筆記

BM算法學習筆記

CSP前就要多學學亂搞(確信)

myy的論文裏引進了BM算法,可以\(n^2\)求出齊次線性遞推式

學習於大佬的博客:

https://www.cnblogs.com/zhouzhendong/p/Berlekamp-Massey.html

https://www.cnblogs.com/zzqsblog/p/6877339.html

本文沒有易證易得,符號非常初等,請放心食用。

我們要求的遞推式形如

\[\forall m< i\leq n,a_i=\sum_{j=1}^m a_{i-j}b_j \]

\(b_i\)即爲遞推式係數。

考慮增量法,設\(R[i]\)表示當前的 \(b\),對於新的一個要求\(a_i\),有兩種情況:

  1. 不用更改,此時\(a_i=\sum_{j=1}^m a_{i-j}R[i][j]\)
  2. 需要更改遞推式,考慮構造遞推式的減量遞推式\(F\)

\(\Delta_i=a_i-\sum_{j=1}^m a_{i-j}R[i][j]\) 爲當前\(i\)計算出多的部分,則減量\(F\)需要滿足:

  1. \[\forall |R'|<k<i,\sum_{j=1}^{|F_j|}a_{k-j}F_j=0 \]

  2. \[\sum_{j=1}^{|F_j|}a_{i-j}F_j=\Delta_i \]

\(F\)需要滿足計算\(a_k(k<i)\)的時候都是0,而計算\(a_i\)的時候是\(\Delta_i\),才能使減去之後\(\Delta'=0\)

如果之前沒出現過需要更改的情況,直接搞上\(i\)個0一定是合法的。(其實也只會在\(i=1\)出現)

否則直接用前面某個\(id\)\(R_{id}\)來構造\(F\)。對於\(R_{id}\),由於定義,其滿足

\[\sum_{j=1}^{|R_{id}|}a_{id-j}R_{id}-a_{id}=\Delta_{id} \]

那麼我們把它除以\(\Delta_{id}\),乘上\(\Delta_i\),再等效替換出 \(i\)

\[\sum_{j=1}^{|R_{id}|}a_{i-(i-id)-j}\frac{R_{id}[j]}{\Delta_{id}}-\frac{a_{id}}{\Delta_{id}}=1\\ \sum_{j=1}^{|R_{id}|}a_{i-(i-id)-j}\frac{R_{id}[j]\Delta_i}{\Delta_{id}}-\frac{a_{id}\Delta_i}{\Delta_{id}}=\Delta_i \]

那麼原來的第\(j\)位變成了第\(i-id+j\)位,相當於右移了\(i-id\)位,再在前面放上一個\(-\frac{\Delta_i}{\Delta_{id}}\)。而對於 \(k<id\) 都是\(\sum_j a_{k-j}R_{id}[j]-a_k\)等於0的情況,這些情況也還是0,因爲乘除對0沒有影響。這樣我們就構造出了滿足要求的\(F\)

那麼當前的答案就是\(R_{i}\)減去\(F\)。注意\(R_i\)並不需要加F,因爲後面還要用。

總結一下,減量F等於

\[\left\{\overbrace{0,0,\cdots,0}^{i-id-1個0},-\frac{\Delta_i}{\Delta_{id}},\frac{R_{id}[1]\Delta_i}{\Delta_{id}},\frac{R_{id}[2]\Delta_i}{\Delta_{id}},\cdots\right\} \]

那麼id選哪個呢?我們要求的是最短遞推式(不然整\(n\)個0也行),那麼選\(i-id+|R_{id}|\)最小的就行了。每次就比較一下\(-i+|R_i|\)更新\(id\),具體看代碼實現。

zzq博客的例子比較生動形象,我就不再造了。

#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x; 
} 
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;i++) 
typedef long double ld;
const int N = 3021;
const int mod = 1e9+7;
int n,pn=0;
ll R[N],Rn[N];
int rl,nl;
ll a[N],dlt[N];
ll tp[N],tl;
int ksm(int a,int b){
	int res=1; 
	while(b){
		if(b&1){
			res=1ll*res*a%mod;
		}a=1ll*a*a%mod;b>>=1;
	}
	return res;
}
const ld eps = 1e-7;
int main(){
	n=read();
	FOR(i,1,n) scanf("%lld",&a[i]);
	int id=0,mi=0;
	FOR(i,1,n){
		ll dlti=(mod-a[i])%mod;
		for(int j=1;j<=rl;j++){
			dlti=(dlti+1ll*a[i-j]*R[j]%mod)%mod;
		}
		dlt[i]=dlti;
		if(dlti==0) continue;
		if(i==1){
			rl=1;R[1]=Rn[1]=0;id=1;nl=rl=1;mi=0;continue;
		}else{
			FOR(j,1,rl) tp[j]=R[j];tl=rl; //備份R 
			int tmp=1ll*dlti*ksm(dlt[id],mod-2)%mod;
			FOR(j,1,nl) R[i-id+j]=(R[i-id+j]-1ll*Rn[j]*tmp%mod+mod)%mod;   //計算F、R 
			R[i-id]=(R[i-id]+tmp)%mod;
			rl=max(rl,i-id+nl);
			if(tl-i<mi) mi=tl-i;nl=tl;FOR(j,1,tl) Rn[j]=tp[j];id=i; //更新Rn 
		}
	}
	while(R[rl]==0) rl--;
	printf("%d\n",rl);
	FOR(i,1,rl){
		printf("%ld ",R[i]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章