首页 > 编程语言 >Bostan-Mori 算法

Bostan-Mori 算法

时间:2024-03-18 22:56:14浏览次数:15  
标签:frac Bostan int 多项式 len 算法 Mori 递推

EI 哥哥科普到 OI 界的科技……最近出现了基于这个的多项式复合/复合逆复杂度的突破,所以今天去看了一下。

这是一个用于解决多项式有理分式的单项系数问题 \([x^n]\frac{P}{Q}\) 的算法。该算法在解决常系数齐次线性递推问题时,代码明显短很多,常数相较原方法极其优越。

首先我们考虑用一次卷积将线性递推数列的生成函数写成有理分式的形式 \(\frac{P}{Q}\)(其中 \(Q\) 是该线性递推特征多项式,卷一下初项截断得到 \(P\))。

然后考虑如何解决 \([x^n]\frac{P}{Q}\)。我们将其写成 \(\frac{P(x)Q(-x)}{Q(x)Q(-x)}\),而 \(W(x)=Q(x)Q(-x)\) 有 \(W(x)=W(-x)\),所以其满足奇数次项都为零。

所以我们将 \(P(x)Q(-x)\) 奇偶次项分开,设 \(P(x)Q(-x)=E(x^2)+xO(x^2),W(x)=Q(x)Q(-x)=T(x^2)\),那么原式就是 \(\frac{E(x^2)}{T(x^2)}+x\frac{O(x^2)}{T(x^2)}\)。

发现左边只会贡献到偶数次项,右边只会贡献到奇数次项。所以我们只需要往一边递归,然后将 \(n\) 变成 \(\lfloor \frac{n}{2}\rfloor\) 继续做就行了。每一层进行常数次长度为线性递推式长度 \(k\) 的卷积,复杂度 \(O(k\log k\log n)\)

而这个算法还可以用于解决特殊形式的有理二元分式求值。

对于 \(F(x,y)=\frac{1}{1-yf(x)}\),我们如果要求 \([x^n]F(x,y)\),可以对 \(x\) 这一维施加 Bostan-Mori 算法,这样迭代 \(t\) 次后 \(x\) 的次数不超过 \(\lfloor \frac{n}{2^t}\rfloor\),\(y\) 的次数不超过 \(2^t\),所以直接进行二元多项式卷积,复杂度就是 \(O(n\log^2 n)\) 的了。

而对于多项式复合/复合逆问题,其转置问题恰好是二元多项式有理分式求值,由于我不太会转置原理所以说没有继续研究了,可以看 alpha1022 博客

常系数齐次线性递推代码:

#include <cstdio>
#include <algorithm>
#include <numeric>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin)),p1==p2?EOF:*p1++)
char buf[1<<22],*p1=buf,*p2=buf;
using namespace std;
int read(){
	char c=getchar();int x=0;bool f=0;
	while(c<48||c>57) f|=(c=='-'),c=getchar();
	do x=x*10+(c^48),c=getchar();
	while(c>=48&&c<=57);
	if(f) return -x;
	return x;
}
const int P=998244353;
typedef long long ll;
typedef unsigned long long ull;
const int N=1<<20;
int cw[N|1],rev[N],ccw[N|1];
int bit,len,ilen;
int n,k;
int qp(int a,int b=P-2){
	int res=1;
	while(b){
		if(b&1) res=(ll)res*a%P;
		a=(ll)a*a%P;b>>=1;
	}
	return res;
}
void init(int x){
	bit=-1;len=1;
	while(len<=x) ++bit,len<<=1;
	int w=qp(3,(P-1)>>(bit+1));
	cw[0]=cw[len]=1;
	for(int i=1;i<len;++i){
		rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);
		cw[i]=(ll)cw[i-1]*w%P;
	}
	ilen=qp(len);
}
void DFT(int *F){
	static ull f[N];
	for(int i=0;i<len;++i) f[i]=F[rev[i]];
	for(int i=1,tt=len>>1;i<len;i<<=1,tt>>=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=j,t=0;k<(j|i);++k,t+=tt){
				ull x=f[k],y=f[k|i]*cw[t]%P;
				f[k]+=y;f[k|i]=x+(P-y);
			}
	for(int i=0;i<len;++i) F[i]=f[i]%P;
}
int A[N],B[N],T[N];
void multi(int *F){
	for(int i=0;i<len;++i) T[i]=F[i];
	DFT(T);reverse(T+1,T+len);
	for(int i=0;i<len;++i) T[i]=(ll)T[i]*ccw[i]%P*ilen%P;
	DFT(T);for(int i=len-1;~i;--i) F[i<<1]=F[i],F[i<<1|1]=T[i];
}
int main(){
	n=read();k=read();
	A[0]=1;
	for(int i=1;i<=k;++i){
		A[i]=(-read())%P;
		if(A[i]<0) A[i]+=P;
	}
	for(int i=0;i<k;++i){
		B[i]=read()%P;
		if(B[i]<0) B[i]+=P;
	}
	init(k<<1);
	DFT(A);DFT(B);
	for(int i=0;i<len;++i) B[i]=(ll)B[i]*A[i]%P;
	for(int i=0;i<=len;++i) ccw[i]=cw[i];
	DFT(B);reverse(B+1,B+len);
	for(int i=0;i<(len>>1);++i) A[i]=A[i<<1],B[i]=(ll)B[i]*ilen%P;
	for(int i=(len>>1);i<len;++i) A[i]=B[i]=0;
	init(k);
	for(int i=k;i<len;++i) B[i]=0;
	DFT(B);
	while(n){
		multi(A);multi(B);
		for(int i=0;i<(len<<1);++i) B[i]=(ll)B[i]*A[i^len]%P;
		for(int i=0;i<len;++i){
			A[i]=(ll)A[i]*A[i|len]%P;
			if(n&1) (B[i]-=B[i|len])<0&&(B[i]+=P);
			else (B[i]+=B[i|len])>=P&&(B[i]-=P);
			if(B[i]&1) B[i]+=P;
			B[i]>>=1;
			A[i|len]=B[i|len]=0;
		}
		if(n&1){
			for(int i=0;i<len;++i) B[i]=(ll)B[i]*ccw[(len<<1)-i]%P;
		}
		n>>=1;
	}
	printf("%d\n",int(accumulate(B,B+len,0ll)%P*qp(accumulate(A,A+len,0ll)%P)%P));
	return 0;
}

标签:frac,Bostan,int,多项式,len,算法,Mori,递推
From: https://www.cnblogs.com/yyyyxh/p/18081638/Bostan_Mori

相关文章

  • 代码随想录算法训练营day27 | leetcode 39. 组合总和、40. 组合总和 II、131. 分割回
    目录题目链接:39.组合总和-中等题目链接:40.组合总和II-中等题目链接:131.分割回文串-中等题目链接:39.组合总和-中等题目描述:给你一个无重复元素的整数数组candidates和一个目标整数target,找出candidates中可以使数字和为目标数target的所有不同组合,并以列表形......
  • 【字符串匹配】BF与KMP算法
    一、字符串匹配问题字符串匹配问题是指在一个主文本字符串中查找一个指定的模式字符串,并确定模式字符串在主文本中出现的位置。这个问题在计算机科学中非常常见,尤其是在文本处理、数据搜索和生物信息学等领域。字符串匹配问题通常涉及到以下几个方面:模式识别:识别主文本中是......
  • AcWing 1171. 距离 Tarjan算法离线求LCA
    题目输入样例1:22121001221输出样例1: 100100输入样例2:32121031151232输出样例2: 1025LCA算法:LCA(LeastCommonAncestors)最近公共祖先Tarjan求LCA是一种离线的算法,也就是说它一遍求出所有需要求的点的LCA,而不是需要求哪两个点再去求......
  • 代码随想录算法训练营第五十天| ● 123.买卖股票的最佳时机III ● 188.买卖股票的
    买卖股票的最佳时机III  题目链接:123.买卖股票的最佳时机III-力扣(LeetCode)思路:与买卖股票2的区别在于我可以买卖两次,那么dp数组的状态就从两种变成了种,即第一次持有,第一次卖出,第二次持有,第二次卖出,注意这四种状态是不会同时存在的,除此之外还有一种状态,那就是不操作。if(......
  • 基于实体抽取-SMC-语义向量的大模型能力评估通用算法(附代码)
    大模型相关目录大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容从0起步,扬帆起航。大模型应用向开发路径及一点个人思考大模型应用开发实用开源项目汇总大模型问答项目问答性能评估方法大模型......
  • C语言随记————简单算法
    1.问题:如何在C语言中实现一个简单的线性查找算法? 答案:线性查找算法可以通过遍历数组的每个元素,逐一比较来查找目标值。以下是一个简单的实现示例:intlinearSearch(intarr[],intn,intx){for(inti=0;i<n;i++){if(arr[i]==x)re......
  • ISIS接口MD5 算法认证实验简述
    默认情况下,ISIS接口认证通过在ISIS协议数据单元(PDU)中添加认证字段,例如:MD5算法,用于验证发送方的身份。ISIS接口认证防止未经授权的设备加入到网络中,并确保邻居之间的通信是可信的。它可以有效地防止路由欺骗和其他恶意攻击。MD5(MessageDigestAlgorithm5)是一种常用的信......
  • Python算法练习
    练习Python算法可以帮助我们提高解决问题的能力、优化代码效率,并深入理解Python语言的特性。以下是一些Python算法练习的建议和示例:排序算法:实现常见的排序算法,如冒泡排序、插入排序、选择排序、快速排序、归并排序等,并比较它们的性能。练习应用排序算法解决实际问题,如查......
  • 算法详解——Dijkstra算法
      Dijkstra算法的目的是寻找单起点最短路径,其策略是贪心加非负加权队列一、单起点最短路径问题  单起点最短路径问题:给定一个加权连通图中的特定起点,目标是找出从该起点到图中所有其他顶点的最短路径集合。需要明确的是,这里关心的不仅仅局限于寻找一条从起点出发到任......
  • 代码随想录算法训练营第23天|669. 修剪二叉搜索树|108.将有序数组转换为二叉搜索树|53
    代码随想录算法训练营第23天|669.修剪二叉搜索树|108.将有序数组转换为二叉搜索树|538.把二叉搜索树转换为累加树|总结篇669.修剪二叉搜索树这道题目比较难,比添加增加和删除节点难的多,建议先看视频理解。题目链接/文章讲解:https://programmercarl.com/0669.%E4%BF%A......