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


~ 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. vue {{}}的用法

    参考链接:https://blog.csdn.net/cofecode/article/details/78666233

  2. easyui datagrid 实现单选并能取消单选

    var intjavadillcheck=0; var rowjavadillselect=null; function ghm_getPageListCallback(result){ if(res ...

  3. golang 切片扩容, 时间复杂度

    在切片扩容时,如果原来的底层数组足够大,能放的下 append 的数据,就不会新建底层数组.而如果不够的话,则会分配一个新的数组.也因此是 O(n) 的时间复杂度

  4. 【转】Entity Framework简介

    Entity Framework Core 可基于现有数据库创建模型,也可基于模型创建数据库. 以下文字来源于:http://www.entityframeworktutorial.net/what- ...

  5. springboot集成elk 二:springboot + elk 采集日志

    到logstash-2.0.0\bin下新建文件 logstash.conf input { tcp { mode => "server" host => " ...

  6. In-App Purchase(iap)快速指南

      点击IOS IAP APP内支付 Java服务端代码直接跳转到示例点击直接跳转到示例 iap简介   在应用中内嵌Store,在iOS应用中使用Store Kit framework来实现In-A ...

  7. 『Python基础练习题』day01

    个人主页: https://lipeiguan.top 1.简述变量命名规范 2.name = input(">>>") name变量是什么数据类型? 3.if条 ...

  8. S03_CH09_DMA_4_Video_Switch视频切换系统

    S03_CH09_DMA_4_Video_Switch视频切换系统 9.1概述 本例程详细创建过程和本季课程第一课<S03_CH01_AXI_DMA_LOOP 环路测试>非常类似,因此如果 ...

  9. 浅谈C++继承

    C++中的继承 1.继承概念及定义:     概念:是面向对象程序设计使代码可以复用的最重要的手段-----继承是类设计层次的复用     定义:            父类->基类:子类-&g ...

  10. (六)Struts的简单异常处理

    一.异常的分类 1.1 struts中的异常概念 Struts的声明式异常: 不处理异常,将异常交给struts框架来处理. 1.2 局部异常 局部异常:异常定义在Action里,异常处理只在这个Ac ...