线性递推的题目区域赛里还是挺多的,还是有必要学一下


~ BM(Berlekamp-Massey)算法 ~

有一个$n$阶线性递推$f$,想要计算$f(m)$,有一种常用的办法是矩阵快速幂,复杂度是$O(n^3logm)$

在不少情况下这已经够用了,但是如果$n$比较大、到了$10^3$级别,这就不太适用了

而BM算法能将这个复杂度压低到$O(n^2logm)$,若加上NTT优化的话能做到$O(n^2+nlognlogm)$,十分厉害

这个算法的核心是将$f(m)$用递推的前$n$项表示

即,已知$f(0),...,f(n-1)$和递推式$f(m)=a_0f(m-1)+...+a_{n-1}f(m-n)$,该算法是求出系数$W_0,...,W_{n-1}$,使得$f(m)=W_0f(n-1)+...+W_{n-1}f(0)$

看似无从下手?实际上只要大力展开就行了

根据定义,有(只是写成$\sum$的形式而已)

\[f(m)=\sum_{i=0}^{n-1}a_i f(m-1-i)\]

而对于每一项再次展开,即

\[f(m-1-i)=\sum_{j=0}^{n-1}a_j f(m-1-i-1-j)\]

全部代入,能得到

\[f(m)=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_ia_j f(m-2-i-j)\]

把式子写的更好看一点,就是

\[f(m)=\sum_{k=0}^{2n-2}\sum_{i+j=k}a_ia_j f(m-2-k)\]

这样做之后有什么用呢?

在原本的递推式中,$f(m)$可以通过$f(m-1),...,f(m-n)$这$n$个项表示

各项展开后,就可以通过$f(m-2),...,f(m-2n)$表示

事实上,我们可以再依次对$f(m-i),2\leq i\leq n$展开,并将系数向$f(m-i-1),...,f(m-i-n)$并入,最终就能把原递推式通过$f(m-n-1),...,f(m-2n)$这$n$项表示

于是可以得到一个新的$n$阶递推式,记为$f(m)=b_0f(m-n+1),...,b_{n-1}f(m-2n)$

再用新递推式将各项展开,就可以通过$f(m-2n-2),...,f(m-4n)$表示

再用原递推式展开$f(m-2n-i),2\leq i\leq n$并向前合并系数,最终就能把原递推式通过$f(m-3n+1),...,f(m-4n)$这$n$项表示

之后都是类似的了,不再赘述

有了上面的思路,就可以用类似快速幂的方法,得到$f(m)=W_0f(m-(k-1)n+1),...,W_{n-1}f(m-kn)$这样的展开式,其中$m-kn<n$

余数$m-kn$是我们不喜欢的,但也没有必要整体再向前推,一开始计算时算出$f(0),...,f(2n-1)$就够了

按照上述思路能这样实现:

#include <cstdio>
#include <cstring>
using namespace std; typedef long long ll;
const int MOD=;
const int N=; int n,m;
int a[N];
int f[N<<]; int tmp[N<<]; void mul(int *y,int *x)
{
memset(tmp,,sizeof(tmp)); for(int i=;i<n;i++)
for(int j=;j<n;j++)
tmp[i+j]=(tmp[i+j]+ll(y[i])*x[j])%MOD; for(int i=;i<n-;i++)
for(int j=;j<n;j++)
tmp[i+j+]=(tmp[i+j+]+ll(tmp[i])*a[j])%MOD; for(int i=;i<n;i++)
y[i]=tmp[i+n-];
} int w[N<<],x[N<<]; int BM()
{
if(m<(n<<))
return f[m]; for(int i=;i<n;i++)
x[i]=a[i],w[i]=a[i]; int t=(m-n)/n;
int rem=m-n-t*n; while(t)
{
if(t&)
mul(w,x);
mul(x,x);
t>>=;
} int res=;
for(int i=;i<n;i++)
res=(res+ll(w[i])*f[rem+n-i-])%MOD;
return res;
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
scanf("%d",&a[i]);
for(int i=;i<n;i++)
scanf("%d",&f[i]);
for(int i=n;i<(n<<);i++)
for(int j=;j<=n;j++)
f[i]=(f[i]+ll(a[j-])*f[n-j])%MOD; printf("%d\n",BM());
return ;
}

想做的更快的话,一个是要写NTT,另一个是合并系数会比较困难,待补


因为这题学的BM:牛客ACM 882B ($Eddy$ $Walker$ $2$)

$m\rightarrow \infty$时,$f(m)\rightarrow \frac{2}{k+1}$ (并不会证...)

从rls那里学了一个证明:

走$k$步,期望能走的长度是$1+2+...+k=\frac{k(k+1)}{2}$

那么在这段距离中,每个位置被走过的概率就是$\frac{k}{\frac{k(k+1)}{2}}=\frac{2}{k+1}$

在其他时候,直接套上面的板子即可

牛客的玄学评测机,同一份代码能差出500ms = =

#include <cstdio>
#include <cstring>
using namespace std; typedef long long ll;
const int MOD=;
const int N=; inline int quickpow(int x,int t)
{
int res=;
while(t)
{
if(t&)
res=ll(res)*x%MOD;
x=ll(x)*x%MOD;
t>>=;
}
return res;
} inline int rev(int x)
{
return quickpow(x,MOD-);
} int n,rn;
ll m;
int a[N];
int f[N<<]; int tmp[N<<]; void mul(int *y,int *x)
{
memset(tmp,,sizeof(tmp)); for(int i=;i<n;i++)
for(int j=;j<n;j++)
tmp[i+j]=(tmp[i+j]+ll(y[i])*x[j])%MOD; for(int i=;i<n-;i++)
for(int j=;j<n;j++)
tmp[i+j+]=(tmp[i+j+]+ll(tmp[i])*a[j])%MOD; for(int i=;i<n;i++)
y[i]=tmp[i+n-];
} int w[N<<],x[N<<]; int BM()
{
if(m<(n<<))
return f[m]; for(int i=;i<n;i++)
x[i]=a[i],w[i]=a[i]; ll t=(m-n)/n;
int rem=m-n-t*n; while(t)
{
if(t&)
mul(w,x);
mul(x,x);
t>>=;
} int res=;
for(int i=;i<n;i++)
res=(res+ll(w[i])*f[rem+n-i-])%MOD;
return res;
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%lld",&n,&m); if(m==-)
{
printf("%d\n",2LL*rev(n+)%MOD);
continue;
} rn=rev(n);
for(int i=;i<n;i++)
a[i]=rn; memset(f,,sizeof(f));
f[]=;
for(int i=;i<(n<<);i++)
for(int j=;j<=n && j<=i;j++)
f[i]=(f[i]+ll(rn)*f[i-j])%MOD; printf("%d\n",BM());
} return ;
}

比较特定的知识点吧,以后遇到就是赚到(然后发现强制NTT,直接白给= =)

(完)

BM(Berlekamp-Massey)算法的更多相关文章

  1. Berlekamp Massey算法求线性递推式

    BM算法求求线性递推式   P5487 线性递推+BM算法   待AC.   Poor God Water   // 题目来源:ACM-ICPC 2018 焦作赛区网络预赛 题意   God Wate ...

  2. BF、KMP、BM、Sunday算法讲解

    BF.KMP.BM.Sunday算法讲解 字串的定位操作通常称作串的模式匹配,是各种串处理系统中最重要的操作之一. 事实上也就是从一个母串中查找一模板串,判定是否存在. 现给出四种匹配算法包括BF(即 ...

  3. Horspool和BM算法解析

    最近算法中学到了Horspool,KMP,BM三种算法.接下来给大家做个分享. Horspool算法: 算法思路: 1.分为匹配串,原串 2.从右往左依次匹配: 一旦遇到不匹配的,原串相对于匹配串 移 ...

  4. BM算法和Sunday快速字符串匹配算法

    BM算法研究了很久了,说实话BM算法的资料还是比较少的,之前找了个资料看了,还是觉得有点生涩难懂,找了篇更好的和算法更好的,总算是把BM算法搞懂了. 1977年,Robert S.Boyer和J St ...

  5. 双目深度估计传统算法流程及OpenCV的编译注意事项

    起因: 1. 双目立体视觉中双目深度估计是非常重要且基础的部分,而传统的立体视觉的算法基本上都在opencv中有相对优秀的实现.同时考虑了性能和效率.因此,学习使用opencv接口是非常重要的. 2. ...

  6. 数据结构 Sunday算法

    Sunday算法是Daniel M.Sunday于1990年提出的字符串模式匹配算法.相对比较KMP和BM算法而言,简单了许多. Sunday算法的思想类似于BM算法中的坏字符思想,有点像其删减版.差 ...

  7. 字符串匹配 - sunday算法

    常见的字符串匹配算法有BF.KMP(教科书中非常经典的).BM.Sunday算法 这里主要想介绍下性能比较好并且实现比较简单的Sunday算法 . 基本原理: 从前往后匹配,如果遇到不匹配情况判断母串 ...

  8. 前端与算法 leetcode 28.实现 strStr()

    # 前端与算法 leetcode 28.实现 strStr() 题目描述 28.移除元素 概要 这道题的意义是实现一个api,不是调api,尽管很多时候api的速度比我们写的快(今天这个我们可以做到和 ...

  9. BF,BM,KMP,就这?

    为保证代码严谨性,文中所有代码均在 leetcode 刷题网站 AC ,大家可以放心食用. 皇上生辰之际,举国同庆,袁记菜馆作为天下第一饭店,所以被选为这次庆典的菜品供应方,这次庆典对于袁记菜馆是一项 ...

  10. [算法2-数组与字符串的查找与匹配] (.NET源码学习)

    [算法2-数组与字符串的查找与匹配] (.NET源码学习) 关键词:1. 数组查找(算法)   2. 字符串查找(算法)   3. C#中的String(源码)   4. 特性Attribute 与内 ...

随机推荐

  1. redis相关操作命令

    一.相关命令列表: 命令原型 时间复杂度 命令描述 返回值 KEYS pattern O(N) 时间复杂度中的N表示数据库中Key的数量.获取所有匹配pattern参数的Keys.需要说明的是,在我们 ...

  2. Java面试 - static 修饰的变量和方法有哪些特点?

    1.static修饰的变量和方法,在类加载时即被初始化,可直接通过类名.变量名和类型.方法名进行调用. 2.static修饰的变量,在类加载时会被分配到数据区的方法区.类的实例可共享方法区中的变量.如 ...

  3. windows下 安装gitlab及其相关图形管理工具

    windows下 安装gitlab及其相关图形管理工具   在windows下安装git中文版客户端并连接gitlab   下载git Windows客户端 git客户端下载地址:https://gi ...

  4. Redis服务监控之RedisLive安装部署(亲测可用)

    一.Redis服务安装部署 1.redis安装(linux系统) 下载 https://redis.io/ 安装依赖 yum install gcc tcl 解压.编译.安装(make & m ...

  5. 【学习笔记】RMQ-Range Minimum/Maximum Query (区间最小/最大值)

    RMQ是一类询问区间最小/最大值的问题. 这类问题一般分成两类:静态区间(无修改),动态区间(带修改). 对于动态区间查询最大/最小,我们显然可以用线段树来解决…… 那么对于静态区间查询最大/最小的问 ...

  6. 基于requests模块的代理

    1.什么是代理? ​ 代理:将网络请求发送给代理服务器,通过代理服务器做中介,将请求转发给目标服务器并将响应返回,从而完成网络通信. 2.为什么使用代理? ​ 使用爬虫抓取批量资源时,在短时间内会对服 ...

  7. SAS学习笔记18 length、lengthn、lengthc函数

  8. JSP开发 路径问题汇总

    //第一种 jsp 表达式 <%=request.getContextPath %> 获取到 web项目名的绝对路径 <!--使用绝对路径的方式引入CSS文件--> <l ...

  9. 1、Java基础:面向对象六大原则

    本文主要介绍了面向对象六大原则. 单一职责原则(Single-Resposibility Principle). “对一个类而言,应该仅有一个引起它变化的原因.”本原则是我们非常熟悉地”高内聚性原则” ...

  10. svg-sprite-loader ( svg-icon) 使用

    svg-sprite-loader 可以多个svg图标合并. 使用时只需根据合并的symbol的id即可.  <svg class="svg-icon" aria-hidde ...