计蒜客模拟赛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(); }