BM算法学习笔记

一种nb算法,可以求出数列的递推式。

具体过程是这样的。

我们先假设它有一个递推式,然后按位去算他的值。

for(int j=0;j<now.size();++j)(delta[i]+=1ll*now[j]*f[i-j-1]%mod)%=mod;

这是我们算出了f[i]应当是多少,但是f[i]有可能不是我们算出的值,所以我们记录一个delta,为我们算出的值减去f[i]的结果。

然后查看一下之前有没有出过锅。

如果出过,那么就补一个0,然后塞过去。。

if(!cnt){now.resize(i);cnt++;continue;}

cnt记录出锅次数,now记录当前递推式。

然后我们需要构造一个递推式把这一位的delta补上。

然后我们设inv为这一次的dalta除以上一次的delta。

然后我们的递推式就是在last和now之间补0,然后加一个inv,后面把所有的pre*(-inv)加进去,这样最后n这个位置会出现-delta就满足我们的要求了。

最后我们把构造递推式和当前递推式加起来。

再贪心选一个左端点最靠右的出锅递推式作为last。

正确性???

代码

#include<iostream>
#include<cstdio>
#include<vector>
#define N 100009
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll n,f[N],delta[N],fail[N],cnt,last;
vector<ll>cur,now,pre;
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline ll power(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;
    }
    return ans;
}
int main(){
    n=rd();
    for(int i=1;i<=n;++i)f[i]=rd(); 
    for(int i=1;i<=n;++i){
       delta[i]=mod-f[i];
       for(int j=0;j<now.size();++j)(delta[i]+=1ll*now[j]*f[i-j-1]%mod)%=mod;
       if(!delta[i])continue;
       fail[cnt]=i;
       if(!cnt){now.resize(i);cnt++;continue;}
       ll inv=((mod-1ll*delta[i]*power(delta[fail[last]],mod-2)%mod)%mod+mod)%mod;
       cur.clear();cur.resize(i-fail[last]-1);cur.push_back(mod-inv);
       for(int j=0;j<pre.size();++j)cur.push_back(1ll*pre[j]*inv%mod);
       if(now.size()>cur.size())cur.resize(now.size());
       for(int j=0;j<now.size();++j)cur[j]+=now[j];
       if(i-now.size()>fail[cnt]-pre.size())pre=now,last=cnt; 
       cnt++;now=cur; 
    } 
    for(int i=0,l=now.size();i<l;++i)printf("%d ",now[i]);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/ZH-comld/p/10306313.html