A Simple Math Problem(矩陣快速冪(模板))
【題目來源】:https://vjudge.net/problem/HDU-1757
【題意】
求解數k對應的f(k)%m,關係式如題面所示。
【思路】
既然給出了遞推式,又因為k的取值上限相當大,所以使用矩陣快速冪來實現f(k)的求解。這個時候就可以用到係數矩陣來表示題面給出的關係式。
什麼是係數矩陣呢?舉個例子(從麻省理工學院線性代數視訊上看的):
假如有三個未知數:x,y,z;
存在如下關係:(先不管是否有解)
2x+3y=z
x-y=2z
那麼用矩陣表示這個關係的話,如下:
然而呢,這就是一個係數矩陣表示方程組。。
假設定為1,2,3矩陣。
所以呢,對於這道題,我們可以根據第二個關係式來推導係數矩陣,也就是1矩陣,然後把它用快速冪的思想求出係數矩陣的k-9次方,最後呢,再乘以2矩陣,得到3矩陣。
f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10)
再上面這個式子裡可以得到:
f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10)
f(x-1)=1*f(x-1)
f(x-2)=1*f(x-2)
f(x-3)=1*f(x-3)
f(x-4)=1*f(x-4)
f(x-5)=1*f(x-5)
f(x-6)=1*f(x-6)
f(x-7)=1*f(x-7)
f(x-8)=1*f(x-8)
f(x-9)=1*f(x-9)
由此得到係數矩陣(也就是1矩陣):
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9
1_ 0_ 0_ 0_ 0_ 0_ 0_ 0_ 0_ 0_
0_ 1_ 0_ 0_ 0_ 0_ 0_ 0_ 0_ 0_
0_ 0_ 1_ 0_ 0_ 0_ 0_ 0_ 0_ 0_
0_ 0_ 0_ 1_ 0_ 0_ 0_ 0_ 0_ 0_
0_ 0_ 0_ 0_ 1_ 0_ 0_ 0_ 0_ 0_
0_ 0_ 0_ 0_ 0_ 1_ 0_ 0_ 0_ 0_
0_ 0_ 0_ 0_ 0_ 0_ 1_ 0_ 0_ 0_
0_ 0_ 0_ 0_ 0_ 0_ 0_ 1_ 0_ 0_
0_ 0_ 0_ 0_ 0_ 0_ 0_ 0_ 1_ 0_
相對應的2矩陣為:
f(x-1) 0 0 0 0 0 0 0 0 0
f(x-2) 0 0 0 0 0 0 0 0 0
f(x-3) 0 0 0 0 0 0 0 0 0
f(x-4) 0 0 0 0 0 0 0 0 0
f(x-5) 0 0 0 0 0 0 0 0 0
f(x-6) 0 0 0 0 0 0 0 0 0
f(x-7) 0 0 0 0 0 0 0 0 0
f(x-8) 0 0 0 0 0 0 0 0 0
f(x-9) 0 0 0 0 0 0 0 0 0
f(x-10)0 0 0 0 0 0 0 0 0
附:
任何矩陣乘以單位矩陣都等於他本身。下面是5*5的單位矩陣:
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1。
然後,。。。。就沒然後了。。。
還有千萬要記得。。。定義矩陣*運算時,定義一個矩陣之後,要先清零。。。
【程式碼】
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<limits.h>
#include<algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int mod=1000000007;
typedef unsigned long long ll;
typedef long long LL;
int k,m;
struct mat
{
int a[11][11];
}ans,temp,base;
mat operator*(mat s,mat t)
{
mat r;
mem(r.a,0);
for(int i=1; i<=10; i++)
{
for(int j=1; j<=10; j++)
{
for(int k=1; k<=10 ; k++)
{
r.a[i][j]=r.a[i][j]+s.a[i][k]*t.a[k][j];
if(r.a[i][j]>=m)
r.a[i][j]%=m;
}
}
}
return r;
}
void print(mat b)
{
for(int i=1;i<=10;i++)
{
for(int j=1;j<=10;j++)
printf("%4d ",b.a[i][j]);
printf("\n");
}
}
void init()
{
mem(ans.a,0);
for(int i=1;i<=10;i++)
ans.a[i][i]=1;
for(int i=2;i<=10;i++)
temp.a[i][i-1]=1;
mem(base.a,0);
for(int i=1;i<=10;i++)
base.a[i][1]=10-i;
}
int pow_mat()
{
init();
while(k)
{
if(k&1)
ans=ans*temp;
temp=temp*temp;
k>>=1;
}
ans=ans*base;
return ans.a[1][1];
}
int main()
{
while(~scanf("%d%d",&k,&m))
{
mem(temp.a,0);
for(int i=1; i<=10; i++)
scanf("%d",&temp.a[1][i]);
if(k<10)
printf("%d\n",k%m);
else
{
k-=9;
printf("%d\n",pow_mat());
}
}
}