SHUOJ好多鸡排

描述

Yaoge买了n块鸡排,其中第n块鸡排的质量为M(n),同时其质量M(n)满足M(n)=f(n)2

已知f(n)=x*f(n-1)+y*f(n-2)。其中,f(0)=1,f(1)=1。

Yaoge希望你能帮他算出这些鸡排的总质量对10007取模后的结果。

输入
第一行输入一个T,表示有T组测试数据(T<=10000),

接下来T行每行输入3个数,n,x,y,(2<=n,x,y<=100000000);

输出
输出鸡排的总质量对10007取模后的结果

我的想法

一开始写这道题时以为只要注意一下余数加法和乘法的使用就好了,然而跑出来一直超时(3000ms都超了),于是周冬雨眉头一皱发现事情并不简单。于是想了很多提高速度的办法,终于在茫茫博客中看到了希望。
这道题主要运用的是以下几个点(萌新觉得C白学了)

  1. 余数
    (a+b)%c=(a%c+b%c)%c;
    ( a*b )%c=(a%c*b%c)%c;
    本题取模是通过除以一个较大的质数(像10007,1e9+7)使结果落在较好观察的int范围里,也能够保证结果的离散性。

  2. 快速乘法
    这是由于数据太大,在机器算乘法时很容易超出long的范围,而且乘法的效率相对加法也较低。于是将其中一个乘数b用二进制表示(11001什么的)。则a*b=a * (2^c1+2^c2+2^c3……),将乘法拆成较小的乘法再相加运算提高速率,再结合上述的余数操作让数据不超出范围。给出代码如下。

  3. 矩阵快速幂
    这和快速乘法类似,同样将A^n的次方数n用二进制表示并拆开计算,不同的则是此处乘法是矩阵乘法,结果矩阵res不是相加而是相乘。

#include<stdio.h>//快速乘法
#define m 10007
int main()
{
    int a,b;
    int res=0;
    scanf("%d %d",&a,&b);
    while(b)
    {
        if(b&1)  //位运算,判断最后位是否为1
        {
            b--;
            res=(res+a)%m; //实现加法
        }
        b=b/2;
        a=(a*2)%m;  //实现乘法
    }
    printf("%d\n",res);
    return 0;
}

算法分析

有了以上的预备知识,本题就相对好解决了。首先我们看以下递推关系。
f(n)=xf(n1)+yf(n2)
S(n) = ∑f(n)^2 = S(n-1)+f(n)^2 = S(n-1)+x^2 f(n-1)^2+y^2 f(n-2)^2+2xyf(n-1)f(n-2)
于是我们构造这样的矩阵方便计算(用矩阵快速幂)
这里写图片描述
用矩阵快速幂计算A^(n-1),我们所求的结果就是该矩阵第一行的和而已了(是不是很简单)

代码

#include<stdio.h>
#define m 10007
struct matrix  //定义矩阵
{
    int data[4][4];
};
struct matrix I={1,0,0,0,   //单位阵
                 0,1,0,0,
                 0,0,1,0,
                 0,0,0,1};
int matpow(struct matrix c,int n); //矩阵快速幂函数
struct matrix mul(struct matrix a,struct matrix b);//矩阵乘法函数
int main()
{
    int t,i;
    scanf("%d",&t);
    for(i=0;i<t;i++)
    {
        long long n,x,y;
        scanf("%ld %ld %ld",&n,&x,&y);
        x=x%m;y=y%m;
            struct matrix a={1,(x*x)%m,(y*y)%m,(2*x*y)%m,  //矩阵A
                             0,(x*x)%m,(y*y)%m,(2*x*y)%m,
                             0,1,0,0,
                             0,x,0,y};
            printf("%d\n",(matpow(a,n-1))%m);
    }
    return 0;
}

struct matrix mul(struct matrix a,struct matrix b)
{
    struct matrix ans=I;
    int i,j,k;
    for(i=0;i<4;i++)
    {
        for(j=0;j<4;j++)
        {
            int temp=0;
            for(k=0;k<4;k++)
            {
                temp=(temp+(a.data[i][k]*b.data[k][j])%m)%m;
            }
            ans.data[i][j]=temp;
        }
    }
    return ans;
}

int matpow(struct matrix c,int n)
{
    int j;
    struct matrix res=I;
     while(n>0)
    {
        if(n&1)
            res=mul(res,c);
        c=mul(c,c);
        n=n/2;
    }
    int ans=0;
    for(j=0;j<4;j++)  //对第一行求和
        ans=(ans+res.data[0][j])%m;
    return ans;
}

在这室外温度40°C的天气里,能写这么一道题学些知识还是很有收获的啊。

猜你喜欢

转载自blog.csdn.net/waveviewer/article/details/76358273