JZOJ 1240. Fibonacci sequence

8 篇文章 0 订阅
2 篇文章 0 订阅

原题

截图

题解

  • 对于80%的数据,根据递推式用 O(n) 的方法就可以了。

  • 对于100%的数据,显然要用到矩阵方法,这里介绍两种思路。

  • F(n)S(n) 分别为 Fibonaccisequence 的第n项和前n项和。

  • 求第x~y项的和相当于求 S(y)S(x1)

  • 于是问题变成了怎么求 S(y) S(x1)

      • S(n) 的通项,和 F(n) 的通项比较。

      • 可以发现 S(n)=F(n+2)1 ,于是就用矩阵乘法求出数列的第 N+2 项。

      • 但这样的方法在比赛中不可取,因为,

      • f(n)=55[(1+52)n(152)n]

      • S(n)=55[(1+52)n+2(152)n+2]1

      • 显然除非你的手算能力足够强,否则这需要浪费大量时间才能推倒出来。

      • 当然,由于 S(n)=F(n+2)1 规律还是优美的。

      • 善于观察的同写几项出来就可以发现规律.

      • 根据 f(n)=f(n1)+f(n2)S(n)=S(n1)+f(n)

      • 我们可以按如下方法构造一个矩阵。

      • [f(n)f(n1)S(n)]=[f(n1)f(n2)S(n1)]110100111

      • 这种方法比较直观,对矩阵乘法比较熟悉的同学应该很容易想到.

Code

#include<cstdio>
#include<cstring>
using namespace std;
int mid[2][2],s[2][2],c[2];
int mid1[2][2],mid2[2][2];
int ans1[2],ans2[2];
const int mo=10000;
inline int read()
{
    int data=0; char ch=0;
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
    return data;
}//读入优化
inline void ksm1(int v)
{
    int ans[2][2];
    ans[0][0]=ans[1][1]=1;
    ans[1][0]=ans[0][1]=0;
    while(v)
    {
        if(v%2)
        {
            memset(s,0,sizeof(s));
            for(int i=0;i<2;i++)
                for(int j=0;j<2;j++)
                    for(int k=0;k<2;k++)
                        s[i][j]=(s[i][j]+ans[i][k]*mid1[k][j])%mo;
            memcpy(ans,s,sizeof(ans));
        }
        memset(s,0,sizeof(s));
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                    s[i][j]=(s[i][j]+mid1[i][k]*mid1[k][j])%mo;
        memcpy(mid1,s,sizeof(mid1));
        v/=2;
    }
    memcpy(mid1,ans,sizeof(mid1));
}//快速幂前x-1项
inline void ksm2(int v)
{
    int ans[2][2];
    ans[0][0]=ans[1][1]=1;
    ans[1][0]=ans[0][1]=0;
    while(v)
    {
        int s[2][2];
        if(v%2)
        {
            memset(s,0,sizeof(s));
            for(int i=0;i<2;i++)
                for(int j=0;j<2;j++)
                    for(int k=0;k<2;k++)
                        s[i][j]=(s[i][j]+ans[i][k]*mid2[k][j])%mo;
            memcpy(ans,s,sizeof(ans));
        }
        memset(s,0,sizeof(s));
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                    s[i][j]=(s[i][j]+mid2[i][k]*mid2[k][j])%mo;
        memcpy(mid2,s,sizeof(mid2));
        v/=2;
    }
    memcpy(mid2,ans,sizeof(mid2));
}//快速幂前y项
int main()
{
    mid[0][1]=mid[1][1]=mid[1][0]=1;
    int T=read();
    while(T--)
    {
        int x=read(),y=read();
        ans1[0]=ans1[1]=ans2[0]=ans2[1]=1;
        memcpy(mid1,mid,sizeof(mid1));
        memcpy(mid2,mid,sizeof(mid2));
        ksm1(x-1);
        ksm2(y);
        memset(c,0,sizeof(c));
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                c[i]=(c[i]+ans1[j]*mid1[i][j])%mo;
        memcpy(ans1,c,sizeof(ans1));
        memset(c,0,sizeof(c));
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                c[i]=(c[i]+ans2[j]*mid2[i][j])%mo;//矩阵乘法
        memcpy(ans2,c,sizeof(ans2));
        printf("%d\n",(ans2[1]+mo-ans1[1])%mo);//相减得区间和
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值