BM算法學習筆記
CSP前就要多學學亂搞(確信)
myy的論文裏引進了BM算法,可以\(n^2\)求出齊次線性遞推式
學習於大佬的博客:
https://www.cnblogs.com/zhouzhendong/p/Berlekamp-Massey.html
https://www.cnblogs.com/zzqsblog/p/6877339.html
本文沒有易證易得,符號非常初等,請放心食用。
我們要求的遞推式形如
\(b_i\)即爲遞推式係數。
考慮增量法,設\(R[i]\)表示當前的 \(b\),對於新的一個要求\(a_i\),有兩種情況:
- 不用更改,此時\(a_i=\sum_{j=1}^m a_{i-j}R[i][j]\)
- 需要更改遞推式,考慮構造遞推式的減量遞推式\(F\)
令\(\Delta_i=a_i-\sum_{j=1}^m a_{i-j}R[i][j]\) 爲當前\(i\)計算出多的部分,則減量\(F\)需要滿足:
-
\[\forall |R'|<k<i,\sum_{j=1}^{|F_j|}a_{k-j}F_j=0 \]
-
\[\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}\),由於定義,其滿足
那麼我們把它除以\(\Delta_{id}\),乘上\(\Delta_i\),再等效替換出 \(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等於
那麼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;
}