佳佳的 Fibonacci

\(f_n=f_{n-1}+f_{n-2},f_1=f_2=1\),求\(f_1+2f_2+3f_3+...+nf_nmod\ m,1≤n,m≤2^{31}-1\)。

数列问题加比较大的数据范围,就很容易到与矩阵快速幂有关,于是尝试变换式子,注意任何小的看起来不起眼的式子变换都有不同的结果,注意递推转移常用的不是策略,而是问题的划分

法一:

设\(t_n=f_1+2f_2+...+nf_n\),有\(t_n=t_{n-1}+nf_n\),现在关键在于求\(nf_n\),根据什么变维护什么的理论,设\(g_n=nf_n=n(f_{n-1}+f_{n-2})=\)

\((n-1)f_{n-1}+(n-2)f_{n-2}+f_{n-1}+2f_{n-2}=g_{n-1}+g_{n-2}+f_{n-1}+2f_{n-2}\),于是我们要想得到t,三个递推都得维护,所以不难有状态矩阵

\[\begin{bmatrix}f_{n-2}&f_{n-1}&g_{n-2}&g_{n-1}&t_{n}\end{bmatrix}
\]

按照填矩阵转移方程套路,不难有转移矩阵

\[\begin{bmatrix}0&1&0&2&0\\1&1&0&1&0\\0&0&0&1&0\\0&0&1&1&1\\0&0&0&0&1\end{bmatrix}
\]

按照基本套路转移即可。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll yyb;
struct matrix{
ll jz[5][5];
il void clear(){
memset(jz,0,sizeof(jz));
}
il void unit(){
clear();
for(ri int i(0);i<5;++i)jz[i][i]|=true;
}
il matrix operator*(matrix x){
matrix y;y.clear();
ri int i,j,k;
for(i=0;i<5;++i)
for(j=0;j<5;++j)
for(k=0;k<5;++k)
(y.jz[i][j]+=jz[i][k]*x.jz[k][j])%=yyb;
return y;
}template<class free>
il matrix operator^(free y){
matrix ans,x(*this);ans.unit();
while(y){
if(y&1)ans=ans*x;
x=x*x,y>>=1;
}return ans;
}
}s,t;
int main(){
s.jz[0][0]=0,s.jz[0][1]=1,s.jz[0][2]=0,s.jz[0][3]=1,s.jz[0][4]=0;
t.jz[0][0]=0,t.jz[0][1]=1,t.jz[0][2]=0,t.jz[0][3]=2,t.jz[0][4]=0;
t.jz[1][0]=1,t.jz[1][1]=1,t.jz[1][2]=0,t.jz[1][3]=1,t.jz[1][4]=0;
t.jz[2][0]=0,t.jz[2][1]=0,t.jz[2][2]=0,t.jz[2][3]=1,t.jz[2][4]=0;
t.jz[3][0]=0,t.jz[3][1]=0,t.jz[3][2]=1,t.jz[3][3]=1,t.jz[3][4]=1;
t.jz[4][0]=0,t.jz[4][1]=0,t.jz[4][2]=0,t.jz[4][3]=0,t.jz[4][4]=1;
ll n;scanf("%lld%lld",&n,&yyb),s=s*(t^n),printf("%lld",s.jz[0][4]);
return 0;
}

法二:

设\(s_n=\sum_{i=1}^nf_i\)

\[f_1+2f_2+3f_3+...+nf_n=s_n+s_n-s_1+s_n-s_2+...s_n-s_{n-1}
\]

\[=ns_n-(s_1+s_2+...+s_{n-1})
\]

前面一截已经可以很好算了,于是考虑变换后面一截,单独拿出来考虑,设

\[g(n)=s_1+s_2+...s_n=g_{n-1}+s_n
\]

\[s_n=s_{n-1}+f_n
\]

\[f_n=f_{n-1}+f_{n-2}
\]

而原式为

\[ans=ns_n-g_{n-1}
\]

所以只要想办法求出这两个东西即可,于是考虑同时转移,所以设状态矩阵

\[\begin{bmatrix}f_n&f_{n+1}&s_n&g_{n-1}\end{bmatrix}
\]

根据填转移矩阵套路,不难有转移矩阵

\[\begin{bmatrix}0&1&0&0\\1&1&1&0\\0&0&1&1\\0&0&0&1\end{bmatrix}
\]

以此转移即可,其实实质就是前缀和套前缀和再套前缀和,解释一下,s为f的前缀和,g是s的前缀和。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll yyb;
struct matrix{
ll jz[4][4];
il void clear(){
memset(jz,0,sizeof(jz));
}
il void unit(){
clear();
for(ri int i(0);i<4;++i)jz[i][i]=1;
}
il matrix operator*(matrix x){
matrix y;y.clear();ri int i,j,k;
for(i=0;i<4;++i)
for(j=0;j<4;++j)
for(k=0;k<4;++k)
(y.jz[i][j]+=jz[i][k]*x.jz[k][j]%yyb)%=yyb;
return y;
}template<class free>
il matrix operator^(free y){
matrix ans,x(*this);ans.unit();
while(y){
if(y&1)ans=ans*x;
x=x*x,y>>=1;
}return ans;
}
}state,tran;
int main(){
ll n,ans;
scanf("%lld%lld",&n,&yyb);
state.jz[0][0]=1,state.jz[0][1]=1,state.jz[0][2]=1,state.jz[0][3]=0;
tran.jz[0][0]=0,tran.jz[0][1]=1,tran.jz[0][2]=0,tran.jz[0][3]=0;
tran.jz[1][0]=1,tran.jz[1][1]=1,tran.jz[1][2]=1,tran.jz[1][3]=0;
tran.jz[2][0]=0,tran.jz[2][1]=0,tran.jz[2][2]=1,tran.jz[2][3]=1;
tran.jz[3][0]=0,tran.jz[3][1]=0,tran.jz[3][2]=0,tran.jz[3][3]=1;
state=state*(tran^n-1),ans=((state.jz[0][2]*n%yyb-state.jz[0][3])%yyb+yyb)%yyb;
printf("%lld",ans);
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll yyb;
struct matrix{
ll jz[4][4];
il void clear(){
memset(jz,0,sizeof(jz));
}
il void unit(){
clear();
for(ri int i(0);i<4;++i)jz[i][i]=1;
}
il matrix operator*(matrix x){
matrix y;y.clear();ri int i,j,k;
for(i=0;i<4;++i)
for(j=0;j<4;++j)
for(k=0;k<4;++k)
(y.jz[i][j]+=jz[i][k]*x.jz[k][j]%yyb)%=yyb;
return y;
}template<class free>
il matrix operator^(free y){
matrix ans,x(*this);ans.unit();
while(y){
if(y&1)ans=ans*x;
x=x*x,y>>=1;
}return ans;
}
}state,tran;
int main(){
ll n,ans;
scanf("%lld%lld",&n,&yyb);
state.jz[0][0]=1,state.jz[0][1]=1,state.jz[0][2]=1,state.jz[0][3]=0;
tran.jz[0][0]=0,tran.jz[0][1]=1,tran.jz[0][2]=0,tran.jz[0][3]=0;
tran.jz[1][0]=1,tran.jz[1][1]=1,tran.jz[1][2]=1,tran.jz[1][3]=0;
tran.jz[2][0]=0,tran.jz[2][1]=0,tran.jz[2][2]=1,tran.jz[2][3]=1;
tran.jz[3][0]=0,tran.jz[3][1]=0,tran.jz[3][2]=0,tran.jz[3][3]=1;
state=state*(tran^n-1),ans=((state.jz[0][2]*n%yyb-state.jz[0][3])%yyb+yyb)%yyb;
printf("%lld",ans);
return 0;
}

法三:

实际上我们可能有些东西不需要转移,递推里面很多式子都会有通向公式,而我们有结论\(s_n=\sum_{i=1}^nf_i=f_{n+2}-f_2\),接着想办法优化,接着法二

\[ans=ns_n-(s_1+s_2+...+s_{n-1})=
\]

\[ns_n-(f_3-f_2+...+f_{n+1}-f_2)=ns_n-(s_{n+1}-n-1)
\]

\[=n(f_{n+2}-f_2)-(f_{n+3}-n-2)=
\]

\[=nf_{n+2}-f_{n+3}+n+2
\]

于是我们只要递推处f就可以算出ans了。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll yyb;
struct matrix{
ll jz[2][2];
il void clear(){
memset(jz,0,sizeof(jz));
}
il void unit(){
clear();
for(ri int i(0);i<2;++i)jz[i][i]|=true;
}
il matrix operator*(matrix x){
matrix y;y.clear();
ri int i,j,k;
for(i=0;i<2;++i)
for(j=0;j<2;++j)
for(k=0;k<2;++k)
(y.jz[i][j]+=jz[i][k]*x.jz[k][j]%yyb)%=yyb;
return y;
}template<class free>
il matrix operator^(free y){
matrix ans,x(*this);ans.unit();
while(y){
if(y&1)ans=ans*x;
x=x*x,y>>=1;
}return ans;
}
}state,tran;
int main(){
ll n,ans;
scanf("%lld%lld",&n,&yyb);
state.jz[0][0]=0,state.jz[0][1]=1;
tran.jz[0][0]=0,tran.jz[0][1]=1;
tran.jz[1][0]=1,tran.jz[1][1]=1;
state=state*(tran^n+2),ans=state.jz[0][0]*n-state.jz[0][1]+2;
printf("%lld",(ans%yyb+yyb)%yyb);
return 0;
}

小结

不难看出随着优化程度的提高,问题解决办法也就越来越间接,矩阵维数也就越来越少,而这个优化的关键在于通项。

佳佳的 Fibonacci的更多相关文章

  1. 佳佳的Fibonacci

    #include<cstdio> #include<cstring> #include<iostream> #include<cmath> #inclu ...

  2. 一本通1644【例 4】佳佳的 Fibonacci

    1644:[例 4]佳佳的 Fibonacci 时间限制: 1000 ms         内存限制: 524288 KB sol:搞了大概一个多小时什么结果都没,被迫去看题解,感觉自己菜到家了qaq ...

  3. TYVJ P3407 佳佳的魔法照片 Label:语文很重要 语文很重要 语文很重要

    描述 佳佳的魔法照片(mphoto.pas\c\cpp) [题目背景] 佳佳的魔法照片(Magic Photo):如果你看过<哈利•波特>,你就会知道魔法世界里的照片是很神奇的.也许是因为 ...

  4. vijosP1285 佳佳的魔法药水

    vijosP1285 佳佳的魔法药水 链接:https://vijos.org/p/1285 [思路] 图论思想. 很巧妙. 如A+B=C,将AB之间连边,边权为C,用以找相连物品与合成物. 用Dij ...

  5. 【DFS】佳佳的魔法阵

    [vijos1284]佳佳的魔法阵 背景 也许是为了捕捉猎物(捕捉MM?),也许是因为其它原因,总之,佳佳准备设计一个魔法阵.而设计魔法阵涉及到的最关键问题,似乎就是那些带有魔力的宝石的摆放…… 描述 ...

  6. P1875 佳佳的魔法药水

    P1875 佳佳的魔法药水 题目描述 发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急 万分!治好 MM 的病只有一种办法,那就是传说中的 0 号药水 ……怎么样才能 ...

  7. vijos:P1285佳佳的魔法药水

    背景 发完了k张照片,佳佳却得到了一个坏消息:他的MM得病了!佳佳和大家一样焦急万分!治好MM的病只有一种办法,那就是传说中的0号药水……怎么样才能得到0号药水呢?你要知道佳佳的家境也不是很好,成本得 ...

  8. 洛谷 P1875 佳佳的魔法药水

    P1875 佳佳的魔法药水 题目描述 发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急 万分!治好 MM 的病只有一种办法,那就是传说中的 0 号药水 --怎么样才能 ...

  9. 洛谷—— P1875 佳佳的魔法药水

    https://www.luogu.org/problemnew/show/1875 题目背景 发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急 万分!治好 MM 的 ...

随机推荐

  1. JS中的迭代(数组)

    啥子是迭代?可以简单地理解为按顺序访问目标(数组.对象等)中的每一项(其实和遍历概念没什么差别) 数组的迭代被我分为两种: 查找 遍历 查找: 1.indexOf(item,start) 该方法搜索指 ...

  2. 知识点整理01- 引用对象被子方法赋值后不改变;CheckBox 取消选择不可用问题

    1. Class 实体是引用类型,但传入方法时是null的情况在子方法中不论怎么赋值当 FirstService.SetPerson(person,ref tempMsg); 执行后Person都是n ...

  3. 深入Dagger:JavaPoet的使用

    前言 最近在用Dagger开发应用,Dagger是google在square的基础上去反射的依赖注入框架. Dagger会根据定义的注解在编译阶段根据依赖注入的配置生成相应的代码,来减少运行期间反射的 ...

  4. TCP建立连接的三次握手和释放连接的四次挥手

    TCP建立连接时,为什么要进行三次握手? 举个打电话的例子: A : 你好我是A,你听得到我在说话吗 B : 听到了,我是B,你听到我在说话吗 A : 嗯,听到了 建立连接,开始聊天! 第一次握手 第 ...

  5. google cloud

    1.win10下安装gcloud SDK 必须设置环境变量CLOUDSDK_PYTHON指向执行文件而不是目录.

  6. Jmeter的安装与环境配置

    1.首先从jmeter的官网http://jmeter.apache.org/download_jmeter.cgi下载jmeter,目前最新版本为5.1,支持的JDK为1.8.. 然后进行解压. 2 ...

  7. 手机号码格式验证和 FASTDFS 工具类

    常见大陆和香港号码格式验证 import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex ...

  8. Linux直接在通过终端打开图片文件

    为了提高效率,减少使用鼠标,有时候想直接通过终端的命令打开一个图片进行查看.可以使用的命令有: eog filename display filename 再使用Alt+F4就可以关闭窗口,尽量达到手 ...

  9. 【leetcode】966. Vowel Spellchecker

    题目如下: Given a wordlist, we want to implement a spellchecker that converts a query word into a correc ...

  10. Delphi 消息函数 SendMessage函数和 PostMessage的区别

    SendMessage函数 将指定的消息发到窗口.它调用特定窗口的窗口处理函数,并且不会立即返回,直到窗口处理函数处理了这个消息. PostMessage函数 将一个消息放入与创建这个窗口的消息队列相 ...