牛客(Nowcoder) 又见斐波那契

H y p e r l i n k Hyperlink Hyperlink

https://ac.nowcoder.com/acm/problem/15666


D e s c r i p t i o n Description Description

给定 f ( 0 ) = 0 , f ( 1 ) = 1 f(0)=0,f(1)=1 f(0)=0,f(1)=1

f i = f i − 1 + f i − 2 + i + i 2 + i 3 + 1 f_i=f_{i-1}+f_{i-2}+i+i^2+i^3+1 fi=fi1+fi2+i+i2+i3+1

f n f_n fn,答案对 1 0 9 + 7 10^9+7 109+7取模

数据范围: n ≤ 1 0 18 n\leq 10^{18} n1018


S o l u t i o n Solution Solution

暴力好打吧。。。

50分

#include
#define mod 1000000007
using namespace std;int n,t;
long long f[1000001];
signed main()
{
    f[0]=0;f[1]=1;
    for(register int i=2;i<=1000000;i++) (f[i]=f[i-1]+f[i-2]+1ll*i*i*i%mod+1ll*i*i%mod+i+1ll)%=mod;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%lld\n",f[n]);
    }
}

其实这道题要是没有 i 3 , i 2 i^3,i^2 i3,i2限制就是 s b sb sb题了,考虑如何发shit规律

把三次方写出来

p3=1 8 27 64 125 216

做差分

add=7 19 37 61 91

二次差分,得到等差数列
gc3=12 18 24 30

同理把平方写出来
p2=1 4 9 16 25 36

差分直接得到等差数列
gc2=3 5 7 9 11 13

于是
1 = 1 1=1 1=1

i = i − 1 + 1 i=i-1+1 i=i1+1

g c 2 i = g c 2 i − 1 + 2 gc2_i=gc2_{i-1}+2 gc2i=gc2i1+2

p 2 i = p 2 i − 1 + g c 2 i − 1 p2_i=p2_{i-1}+gc2_{i-1} p2i=p2i1+gc2i1

g c 3 i = g c 3 i − 1 + 6 gc3_i=gc3_{i-1}+6 gc3i=gc3i1+6

a d d i = a d d i − 1 + g c 3 i − 1 add_i=add_{i-1}+gc3_{i-1} addi=addi1+gc3i1

$p3_i=p3_{i-1}+add_{i-1} $

f i − 1 = f i − 1 f_{i-1}=f_{i-1} fi1=fi1

f i = f i − 1 + f i − 2 + p 3 i − 1 + a d d i − 1 + p 2 i − 1 + g c 2 i − 1 + i − 1 + 2 f_i=f_{i-1}+f_{i-2}+p3_{i-1}+add_{i-1}+p2_{i-1}+gc2_{i-1}+i-1+2 fi=fi1+fi2+p3i1+addi1+p2i1+gc2i1+i1+2

矩阵转移即可

牛客(Nowcoder) 又见斐波那契_第1张图片

注意到我们最少从第二项开始转移,所以预处理 i = = 2 i==2 i==2时的各项数组

1 = 1 1=1 1=1
i = 2 i=2 i=2
g c 2 i = 5 gc2_i=5 gc2i=5
g c 3 i = 18 gc3_i=18 gc3i=18
a d d i = 19 add_i=19 addi=19
p 2 i = 4 p2_i=4 p2i=4
p 3 i = 8 p3_i=8 p3i=8

后面这两个往后移一位(这样方便转移,否则就要将原递推式进一步拆分)
f i − 2 = 1 f_{i-2}=1 fi2=1
f i − 1 = 16 f_{i-1}=16 fi1=16

时间复杂度: O ( T k 3 l o g n ) O(Tk^3logn) O(Tk3logn)

在作者的解法中 k = 9 k=9 k=9,有些最终结果并非直接得到而是间接转移的 k = 6 k=6 k=6,但是矩阵会麻烦很多

一些杂七杂八的话:这道题是作者在一站到底的时候刷的第一题,最后一分钟过了,真的刺激。。。。


C o d e Code Code

#include
#include
#define ymw 1000000007
using namespace std;int t;
long long n;
struct node{long long a[9][9];};
const int d[9][9]=
{
	{1,1,2,6,0,0,0,0,2},
	{0,1,0,0,0,0,0,0,1},
	{0,0,1,0,0,1,0,0,1},
	{0,0,0,1,1,0,0,0,0},
	{0,0,0,0,1,0,1,0,1},
	{0,0,0,0,0,1,0,0,1},
	{0,0,0,0,0,0,1,0,1},
	{0,0,0,0,0,0,0,0,1},
	{0,0,0,0,0,0,0,1,1},
};
const int e[9]={1,2,5,18,19,4,8,1,16};
inline node mul(node x,node y)
{
    node z;
    memset(&z,0,sizeof(z));
    for(register int i=0;i<9;i++)
     for(register int j=0;j<9;j++)
      for(register int k=0;k<9;k++)
    z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%ymw)%ymw;
    return z;
}
inline long long ksm(register long long y)
{
    node x,ans;
    memset(&x,0,sizeof(x));
    memset(&ans,0,sizeof(ans));
    for(register int i=0;i<9;i++)
     for(register int j=0;j<9;j++)
      x.a[i][j]=d[i][j];
    for(register int i=0;i<9;i++) ans.a[0][i]=e[i];
    while(y)
    {
        if(y&1) ans=mul(ans,x);
        x=mul(x,x);
        y>>=1;
    }
    return ans.a[0][8];
}
signed main()
{
	scanf("%d",&t);
	while(t--)
	{
    	scanf("%lld",&n);
    	if(n==0) putchar(48);
    	else if(n==1) putchar(49);
    	else printf("%lld",ksm(n-2));
    	putchar(10);
    }
}

你可能感兴趣的:(矩阵乘法加速递推)