2020牛客寒假算法基础集训营1 J题 u's的影响力【矩阵快速幂+欧拉降幂】(全网最详细题解,完整思路)_已知斐波那契数列 f n =f n 1 f n 2 (n>=3),f 1 =1,f 2 =1 求解该-程序员宅基地

技术标签: 矩阵快速幂  斐波那契数列  算法  扩展欧拉定理  欧拉降幂  # ACM-数学  

题目传送门:https://ac.nowcoder.com/acm/contest/3002/J

在这里插入图片描述

牛客“基础”算法训练营出的题很有意思啊,非常适合我这种萌新学习(真的十分“基础”,我差点就信了)。

今天终于把这题给补了,这相当于是一个模板套模板的题,下面说说我的思路。

完整思路:

首先写出前几项的表达式,找一下规律。

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)=x1y1(ab)1

f ( 4 ) = x 1 ∗ y 2 ∗ ( a b ) 2 f(4)=x^1*y^2*(a^b)^{2} f(4)=x1y2(ab)2

f ( 5 ) = x 2 ∗ y 3 ∗ ( a b ) 4 f(5)=x^2*y^3*(a^b)^{4} f(5)=x2y3(ab)4

f ( 6 ) = x 3 ∗ y 5 ∗ ( a b ) 7 f(6)=x^3*y^5*(a^b)^{7} f(6)=x3y5(ab)7

f ( 7 ) = x 5 ∗ y 8 ∗ ( a b ) 12 f(7)=x^5*y^8*(a^b)^{12} f(7)=x5y8(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)=xk1yk2(ab)k3

对于k1满足的斐波那契数列F(n),有:

F ( 1 ) = 1 , F ( 2 ) = 1 F(1)=1,F(2)=1 F(1)=1F(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(n1)+F(n2)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(n1)F(n2)]=[F(n)F(n1)]

把常数矩阵设为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(n1)F(n2)]=[F(n)F(n1)]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} An2[F(2)F(1)]=[F(n)F(n1)](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(n1)](n>=2)S=An2

那么 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)=S00F(2)+S01F(1)=S00+S01
这样我们就能在 O ( 8 ∗ l o g n ) O(8*logn) O(8logn) 的复杂度下求出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)=xk1yk2(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取模。

AC代码:(4ms)

#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…)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ljw_study_in_CSDN/article/details/104475316

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读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

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签