关于求阶乘逆元
以下思路背景均来自题目牡牛和牝牛
-
预处理做法
众所周知:
众所周知知:
c++中除法很不稳定,所以需要逆元。
所以真正在运用时,我们的函数会这么写:
int C(int a,int b)
{
if(a<b)
return 0;
return jc[a]*ny[b]%mod*ny[a-b]%mod;
}
啊哦忘说要取模了
这样写看着十分简便,不过需要预处理出
而这个范围往往是很大的,一般情况下处理范围为
所以,范围右边界取值,应为但是也有太大导致开不了数组的情况,这就得下面优化了。
故有:
jc[0]=ny[0]=1;
for(int i=1;i<=mod-1;i++)
jc[i]=jc[i-1]*i%mod;
ny[mod-1]=iv(jc[mod-1]);
for(int i=mod-2;i>=1;i--)
ny[i]=ny[i+1]*(i+1)%mod;
而这之中又出现了一个新的函数取逆元,它需要用到
void exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return;
}
int x0,y0;
exgcd(b,a%b,x0,y0);
x=y0;
y=x0-(a/b)*y0;
}
int iv(int a)
{
int x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
这种写法的好处在于预处理已经做完了几乎全部工作,整个程序的复杂度集中在这里,之后再进行大量运用
但问题也很明显:当
例如
-
预处理优化
一个小优化:可以只需预处理阶乘。
预处理为:
jc[0]=1;
for(int i=1;i<=maxn;i++)
jc[i]=jc[i-1]*i%mod;
当然,这里范围取
组合函数为:
int C(int n,int m){
if(n<m)
return 0;
return jc[n]*qpow(jc[m]*jc[n-m]%mod,mod-2)%mod;
}
少预处理一个数组的时间优化还是挺多的。
这道题
-
优化做法
优化后时间已经很短了,但有一种情况,它还是不能实现:
这时候,就有了另一种处理方式:优化掉预处理,在每次应用函数时算出每个阶乘。
它的特点也很明显:在处理单次或少次询问时有着更优的时间复杂度;也因此,它在询问次数较多时表现就很不尽人意了。
实现方法:
与上方优化后一样,用快速幂求逆元,只需要一个快速幂函数,而且不需要预处理,只是组合函数内东西稍微多了点。
int C(int a,int b)
{
int aa=1,bb=1;
fo(i,a-b+1,a)//这是化简后的阶乘结果
aa=aa*i%mod;
fo(i,1,b)
bb=bb*i%mod;
return aa*qpow(bb,mod-2)%mod;
}
-
总结
上面三种做法都很常用,主要依据于大家自己的习惯。
做题时应先根据题意做出判断,如果预处理的范围超过了数组能承受的大小,则只能选用(大概)第三种方法;若询问次数较多,则前两种方法更优。
所以:寸有所长 尺有所短,还是都记下来最保险。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】