一种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; }