• 计蒜客模拟赛D2T1 蒜头君的兔子:矩阵快速幂


    题目链接:https://nanti.jisuanke.com/t/16442

    题意:

      有个人在第一年送了你一对1岁的兔子。这种兔子刚生下来的时候算0岁,当它在2~10岁的时候,每年都会生下一对兔子,并且它在10岁那年生完兔子后就会挂掉。现在让你算出第t年兔子的总数(不算那一年10岁的兔子)。

    题解:

      我们用一个1*10的矩阵代表某一年的兔子数量,第k列上的数字n代表今年有n只k岁的兔子。

      那么初始矩阵是这样的:

      

      接下来考虑怎样构造特殊矩阵。

      有两个转移关系:

        第二年0岁的兔子数 = 第二年2~10岁的兔子数之和 = 今年1~9岁的兔子数之和

        第二年k (1<=k<=10) 岁的兔子数 = 今年k-1岁的兔子数

      也就是这样转移:

      

      b[0] = a[1] + a[2] + ... + a[9]

      b[1] = a[0]

      b[2] = a[1]

      ...

      b[9] = a[8]

      b[10] = a[9]

      那么特殊矩阵也就出来了:

      

      所以第t年的矩阵ans = 初始矩阵start * ( 特殊矩阵special ^ (t-1) )

      优化:由于在整个过程中根本没有用到每年10岁的兔子数量,所以可以省去初始矩阵的第10列,以及特殊矩阵的第10列&第10行。

    AC Code:

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #define MAX_L 15
    #define MOD 1000000007
    
    using namespace std;
    
    struct Mat
    {
        int n;
        int m;
        long long v[MAX_L][MAX_L];
        Mat()
        {
            memset(v,0,sizeof(v));
            n=0;
            m=0;
        }
    };
    
    int t;
    long long sum=0;
    
    Mat make_unit(int k)
    {
        Mat mat;
        mat.n=k;
        mat.m=k;
        for(int i=0;i<k;i++)
        {
            mat.v[i][i]=1;
        }
        return mat;
    }
    
    Mat make_start()
    {
        Mat mat;
        mat.n=1;
        mat.m=10;
        mat.v[0][1]=1;
        return mat;
    }
    
    Mat make_special()
    {
        Mat mat;
        mat.n=10;
        mat.m=10;
        for(int i=1;i<=9;i++)
        {
            mat.v[i][0]=1;
            mat.v[i-1][i]=1;
        }
        return mat;
    }
    
    Mat mul_mat(const Mat &a,const Mat &b)
    {
        Mat c;
        c.n=a.n;
        c.m=b.m;
        for(int i=0;i<a.n;i++)
        {
            for(int j=0;j<b.m;j++)
            {
                for(int k=0;k<a.m;k++)
                {
                    c.v[i][j]+=(a.v[i][k]*b.v[k][j])%MOD;
                    c.v[i][j]%=MOD;
                }
            }
        }
        return c;
    }
    
    Mat quick_pow_mat(Mat mat,long long k)
    {
        Mat ans;
        ans=make_unit(mat.n);
        while(k)
        {
            if(k&1)
            {
                ans=mul_mat(ans,mat);
            }
            k>>=1;
            mat=mul_mat(mat,mat);
        }
        return ans;
    }
    
    void read()
    {
        cin>>t;
    }
    
    void solve()
    {
        Mat start=make_start();
        Mat special=make_special();
        Mat ans=mul_mat(start,quick_pow_mat(special,t-1));
        for(int i=0;i<=9;i++)
        {
            sum=(sum+ans.v[0][i])%MOD;
        }
    }
    
    void print()
    {
        cout<<sum<<endl;
    }
    
    int main()
    {
        read();
        solve();
        print();
    }
  • 相关阅读:
    常用排序算法(七)归并排序
    常用排序算法(六)堆排序
    常用排序算法(五)选择排序
    常用排序算法(四)希尔排序
    常用排序算法(三)直接插入排序
    常用排序算法(二)快速排序
    常用排序算法(一)冒泡排序
    Linux内核中双向链表的经典实现
    回溯法
    迪杰斯特拉Dijkstra算法介绍
  • 原文地址:https://www.cnblogs.com/Leohh/p/7261166.html
Copyright © 2020-2023  润新知