技术标签: 矩阵快速幂 斐波那契数列 算法 扩展欧拉定理 欧拉降幂 # ACM-数学
牛客“基础”算法训练营出的题很有意思啊,非常适合我这种萌新学习(真的十分“基础”,我差点就信了)。
今天终于把这题给补了,这相当于是一个模板套模板的题,下面说说我的思路。
首先写出前几项的表达式,找一下规律。
f ( 1 ) = x f(1)=x f(1)=x
f ( 2 ) = y f(2)=y f(2)=y
f ( 3 ) = x 1 ∗ y 1 ∗ ( a b ) 1 f(3)=x^1*y^1*(a^b)^1 f(3)=x1∗y1∗(ab)1
f ( 4 ) = x 1 ∗ y 2 ∗ ( a b ) 2 f(4)=x^1*y^2*(a^b)^{2} f(4)=x1∗y2∗(ab)2
f ( 5 ) = x 2 ∗ y 3 ∗ ( a b ) 4 f(5)=x^2*y^3*(a^b)^{4} f(5)=x2∗y3∗(ab)4
f ( 6 ) = x 3 ∗ y 5 ∗ ( a b ) 7 f(6)=x^3*y^5*(a^b)^{7} f(6)=x3∗y5∗(ab)7
f ( 7 ) = x 5 ∗ y 8 ∗ ( a b ) 12 f(7)=x^5*y^8*(a^b)^{12} f(7)=x5∗y8∗(ab)12
写到这里,可以看出很明显的规律了:从第三项开始,x,y的指数是斐波那契数列, 比如说x的指数从第三项开始依次是1,1,2,3,5,y的指数从第三项开始依次是1,2,3,5,8。
把ab看成一个整体,ab的指数 = x的指数 + y的指数 - 1。
我们先求x的指数,因为求了x的指数,就能求y的指数,y的指数是x的指数对应的斐波那契数列的后一项,接着ab的指数也可以算出来。
为了方便表述,设x的指数为k1,y的指数为k2,ab的指数为k3,当n>=3时,有:
f ( n ) = x k 1 ∗ y k 2 ∗ ( a b ) k 3 f(n)=x^{k1}*y^{k2}*(a^b)^{k3} f(n)=xk1∗yk2∗(ab)k3
对于k1满足的斐波那契数列F(n),有:
F ( 1 ) = 1 , F ( 2 ) = 1 F(1)=1,F(2)=1 F(1)=1,F(2)=1
F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n > = 3 ) F(n)=F(n-1)+F(n-2)(n>=3) F(n)=F(n−1)+F(n−2)(n>=3)
单独研究F(n),这就是矩阵快速幂的模板题了,先把递推式 F(n)=F(n-1)+F(n-2) 写成矩阵相乘形式:
[ 1 1 1 0 ] ∗ [ F ( n − 1 ) F ( n − 2 ) ] = [ F ( n ) F ( n − 1 ) ] \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} * \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} = \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix} [1110]∗[F(n−1)F(n−2)]=[F(n)F(n−1)]
把常数矩阵设为A,写成:
A ∗ [ F ( n − 1 ) F ( n − 2 ) ] = [ F ( n ) F ( n − 1 ) ] , 其 中 A = [ 1 1 1 0 ] A* \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} = \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix},其中A=\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} A∗[F(n−1)F(n−2)]=[F(n)F(n−1)],其中A=[1110]
当n>=2时(若n=2,矩阵A的0次幂为单位矩阵E),有:
A n − 2 ∗ [ F ( 2 ) F ( 1 ) ] = [ F ( n ) F ( n − 1 ) ] ( n > = 2 ) , 其 中 A = [ 1 1 1 0 ] A^{n-2}* \begin{bmatrix} F(2) \\ F(1) \\ \end{bmatrix} = \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix}(n>=2),其中A=\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} An−2∗[F(2)F(1)]=[F(n)F(n−1)](n>=2),其中A=[1110]
得到了以上的式子,那么矩阵An-2可以直接套用矩阵快速幂,F(2),F(1)已知,假设An-2=S(矩阵下标从0开始),有:
[ S 00 S 01 S 10 S 11 ] ∗ [ F ( 2 ) F ( 1 ) ] = [ F ( n ) F ( n − 1 ) ] ( n > = 2 ) , 其 中 S = A n − 2 \begin{bmatrix}S_{00} & S_{01} \\ S_{10} &S_{11} \\ \end{bmatrix} * \begin{bmatrix} F(2) \\ F(1) \\ \end{bmatrix} = \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix}(n>=2),其中S=A^{n-2} [S00S10S01S11]∗[F(2)F(1)]=[F(n)F(n−1)](n>=2),其中S=An−2
那么 F ( n ) = S 00 ∗ F ( 2 ) + S 01 ∗ F ( 1 ) = S 00 + S 01 F(n)=S_{00}*F(2)+S_{01}*F(1)=S_{00}+S_{01} F(n)=S00∗F(2)+S01∗F(1)=S00+S01。
这样我们就能在 O ( 8 ∗ l o g n ) O(8*logn) O(8∗logn) 的复杂度下求出F(n)。(快速幂是logn,每次2*2的两个矩阵相乘有三层循环为8)
接下来,注意下标的转换。因为F(n)的下标是从1开始的,但是f(n)的下标是从3开始才是斐波那契数列,所以题目输入的n要减去2才是x的指数在斐波那契数列中的下标,所以x的指数k1=F(n-2),y的指数k2=F(n-1), 注意前提是必须满足 n-2>=2&&n-1>=2 即 n>=4。当n<4时特判即可。
注意运算结果f(n)要对1e9+7取模,设p=1e9+7,有:
f ( n ) = x k 1 ∗ y k 2 ∗ ( a b ) k 3 % p f(n)=x^{k1}*y^{k2}*(a^b)^{k3} \%p f(n)=xk1∗yk2∗(ab)k3%p
这个时候需要用到欧拉降幂了。p为素数,则 φ(p)=p-1。如果底数x与p不互质,那么x%p==0,答案一定是0;如果底数x与p互质,那么可以直接对指数进行降幂,也就是把指数k1对φ(p)取模。
好,做到这里,这道题基本上就做完了。
写代码的时候可以先把底数x,y,a对p取模,特判有0的情况。还有要特判n<4的答案,因为这时候不能用矩阵快速幂。最后就是注意写快速幂取模的时候想清楚是对p取模,还是对φ(p)取模,矩阵快速幂是为了求指数,所以矩阵快速幂要对φ(p)取模,降幂之后的公式用普通快速幂,是对p取模。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll p=1e9+7,mod=p-1;//矩阵快速幂对mod取模,底数对p取模
ll n,x,y,a,b;
struct node
{
ll m[2][2];
};
node E={
//单位矩阵
1,0,
0,1};
node A={
//斐波那契基础矩阵
1,1,
1,0};
node mul(node a,node b)//两矩阵相乘
{
node s;
memset(s.m,0,sizeof(s.m));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
s.m[i][j]=(s.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod;
return s;
}
node matrix_pow(node a,ll b)//矩阵快速幂,求矩阵a的b次幂
{
node s=E;
while(b)
{
if(b&1)s=mul(s,a);
b=b/2;
a=mul(a,a);
}
return s;
}
ll qpow(ll a,ll b)//普通快速幂,求a^b%p
{
ll s=1;
while(b)
{
if(b&1)s=s*a%p;
b=b/2;
a=a*a%p;
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>x>>y>>a>>b;
x=x%p;
y=y%p;
a=a%p;
if(x==0||y==0||a==0)printf("0\n");
else if(n==1)printf("%lld\n",x);
else if(n==2)printf("%lld\n",y);
else if(n==3)printf("%lld\n",x*y%p*qpow(a,b)%p);
else
{
ll kx=n-2;//x指数是在斐波那契第几项
ll ky=n-1;//y指数是在斐波那契第几项
node s1=matrix_pow(A,kx-2);
ll k1=(s1.m[0][0]+s1.m[0][1])%mod;//x降幂后的指数
node s2=matrix_pow(A,ky-2);
ll k2=(s2.m[0][0]+s2.m[0][1])%mod;//y降幂后的指数
ll k3=(k1+k2-1+mod)%mod;//a^b降幂后的指数
ll t=qpow(a,b);
ll ans=qpow(x,k1)*qpow(y,k2)%p*qpow(t,k3)%p;
printf("%lld\n",ans);
}
return 0;
}
其实这题挺多人都过了,我也不知道为什么要把这“水题”写得这么详细…主要还是自己太菜了。
希望以后见到这种题不要怕,想清楚思路,下次能一发AC(立起了flag…)
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数