BM(Berlekamp-Massey)算法
线性递推的题目区域赛里还是挺多的,还是有必要学一下
~ 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)算法的更多相关文章
- Berlekamp Massey算法求线性递推式
BM算法求求线性递推式 P5487 线性递推+BM算法 待AC. Poor God Water // 题目来源:ACM-ICPC 2018 焦作赛区网络预赛 题意 God Wate ...
- BF、KMP、BM、Sunday算法讲解
BF.KMP.BM.Sunday算法讲解 字串的定位操作通常称作串的模式匹配,是各种串处理系统中最重要的操作之一. 事实上也就是从一个母串中查找一模板串,判定是否存在. 现给出四种匹配算法包括BF(即 ...
- Horspool和BM算法解析
最近算法中学到了Horspool,KMP,BM三种算法.接下来给大家做个分享. Horspool算法: 算法思路: 1.分为匹配串,原串 2.从右往左依次匹配: 一旦遇到不匹配的,原串相对于匹配串 移 ...
- BM算法和Sunday快速字符串匹配算法
BM算法研究了很久了,说实话BM算法的资料还是比较少的,之前找了个资料看了,还是觉得有点生涩难懂,找了篇更好的和算法更好的,总算是把BM算法搞懂了. 1977年,Robert S.Boyer和J St ...
- 双目深度估计传统算法流程及OpenCV的编译注意事项
起因: 1. 双目立体视觉中双目深度估计是非常重要且基础的部分,而传统的立体视觉的算法基本上都在opencv中有相对优秀的实现.同时考虑了性能和效率.因此,学习使用opencv接口是非常重要的. 2. ...
- 数据结构 Sunday算法
Sunday算法是Daniel M.Sunday于1990年提出的字符串模式匹配算法.相对比较KMP和BM算法而言,简单了许多. Sunday算法的思想类似于BM算法中的坏字符思想,有点像其删减版.差 ...
- 字符串匹配 - sunday算法
常见的字符串匹配算法有BF.KMP(教科书中非常经典的).BM.Sunday算法 这里主要想介绍下性能比较好并且实现比较简单的Sunday算法 . 基本原理: 从前往后匹配,如果遇到不匹配情况判断母串 ...
- 前端与算法 leetcode 28.实现 strStr()
# 前端与算法 leetcode 28.实现 strStr() 题目描述 28.移除元素 概要 这道题的意义是实现一个api,不是调api,尽管很多时候api的速度比我们写的快(今天这个我们可以做到和 ...
- BF,BM,KMP,就这?
为保证代码严谨性,文中所有代码均在 leetcode 刷题网站 AC ,大家可以放心食用. 皇上生辰之际,举国同庆,袁记菜馆作为天下第一饭店,所以被选为这次庆典的菜品供应方,这次庆典对于袁记菜馆是一项 ...
- [算法2-数组与字符串的查找与匹配] (.NET源码学习)
[算法2-数组与字符串的查找与匹配] (.NET源码学习) 关键词:1. 数组查找(算法) 2. 字符串查找(算法) 3. C#中的String(源码) 4. 特性Attribute 与内 ...
随机推荐
- 【VS开发】【C++开发】正确使用auto_ptr智能指针
1, auto_ptr类 auto_ptr是一个模板类,定义如下: template <typename Type>class auto_ptr {...}: 它存储的是一个指向Type的 ...
- IBM.WMQ订阅消息
网上关于IBM这个消息队列中间件的资料相对比较少,C#相关的资料就更少了,最近因为要对接这个队列中间件,花了不少功夫去搜索.整理各种资料,遇到很多问题,因此记录下来. 1.基于 amqmdnet.dl ...
- JavaScript代码document.all(i).tagName
在ie内核的浏览器当中,下面的代码支持document.all(i).tagName.toLowerCase(); 但在火狐浏览器当中,不支持上面的代码,所以需要用下面的一行代码来代替上面的代码.do ...
- jqGrid获取展示的所有行id集合
$("#jqGrid").getDataIDs();
- shell将txt转换为csv
cat aa.txt | tr -s '[:blank:]' ',' > bb.csv
- TCP 客户端编程
1.Qt中TCP客户端编程 对Qt编程而言,网络只是数据传输的通道: Qt提供了QTcpSocket类(封装了TCP协议细节): 将QTcpSocket的对象当做黑盒使用,进行数据首发. 1.1QTc ...
- Hibernate一对多自关联、多对多关联
今天分享hibernate框架的两个关联关系 多对多关系注意事项 一定要定义一个主控方 多对多删除 主控方直接删除 被控方先通过主控方解除多对多关系,再删除被控方 禁用级联删除 关联关系编辑,不 ...
- 普通表分区改造_rename方式
一.需求 配合开发人员,对业务临时表进行分区改造(业务认为的临时表,只需要保留近一月数据,并非oracle临时表类型) 二.如下记录完整过程 开发需求 TS_PM 以time_key分区 .沟通明确方 ...
- springboot中使用验证码kaptcha
1.pom.xml引入kaptcha所需要的jar包 <!-- 验证码 --> <dependency> <groupId>com.github.penggle&l ...
- 每次开机都要按F1的解决办法
买了个新的硬盘来装电脑,装操作系统时到微软官网下载了WIN10放在U盘里制作成系统安装盘,具体操作自己百度.装好了之后发现每次开机都要按一下F1,百度了很多都没用, 一次偶然的机会,我拆开了电脑主机硬 ...