1. 程式人生 > 實用技巧 >洛谷P1397 [NOI2013]矩陣遊戲

洛谷P1397 [NOI2013]矩陣遊戲

題意連結

題意簡述

給定一個二維的遞推方程,已知第一項\((1,1)\)求其第\((n,m)\)

分析

一道矩陣乘法基礎題

一看到遞推式還沒有特殊條件就知道是矩陣快速冪

但是這個\(n,m\)的範圍著實是不正常

所以考慮到要用個某某定理或者性質把\(n,m\)的範圍縮小

這裡就是費馬小定理了

容易發現,這裡的矩陣乘法是滿足費馬小定理的,但是當\(a=1\)的情況要特判,(至於為什麼詳見這篇部落格或者這篇部落格

所以接下來我們可以開始構造狀態矩陣

構造狀態轉移矩陣的方法:若狀態矩陣中第 \(x\) 個數對下一單位時間狀態矩陣中第 \(y\) 個數有影響,則把轉移矩陣的第 \(x\)行第 \(y\)

列賦值為適當的係數。

我們發現,狀態矩陣中第一個數要乘\(a\)\(b\),那麼根據上面的定義很容易想到,狀態轉移矩陣的第一列分別就是 \(a\)\(b\),分別對應\(f_{(i-1,j)}\)\(1\)。我們會發現狀態矩陣中第二個值是不變的,它賦值為 \(1\) 的意義在於能夠剛好計算最後的 \(+b\)

所以我們可以把單獨一行的轉移方程寫出來:

\(\left\{ \begin{matrix} a & 0\\ b & 1 \end{matrix} \right\}\)

那麼其實我們每一行轉移過後就是行與行之間的轉移,那麼我們可以把這個轉移方程也直接寫出來:

\(\left\{ \begin{matrix} c & 0\\ d & 1 \end{matrix} \right\}\)

(其實一點變化都沒有)

所以我們手摸一下,可以直接得出:

我們的初始狀態的矩陣要乘上

\[\left[ \begin{matrix} a & 0\\ b & 1 \end{matrix} \right]^{(m-1)*n} * \left[ \begin{matrix} c & 0\\ d & 1 \end{matrix} \right]^{(n-1)} \]

最後答案就是初始矩陣的元素\(f_{(1,1)}\)

那麼這道題就結束了

程式碼

程式碼附上

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
const ll MOD=1e9+7,N=1e6+5;
#define inc(a,b) (a+b>=MOD?a+b-MOD:a+b)
struct Matrix{
	ll a[3][3];
	Matrix(){memset(a,0,sizeof(a));}
	Matrix operator * (Matrix B){
		Matrix C;
		for(int i=1;i<3;i++){
			for(int j=1;j<3;j++){
				for(int k=1;k<3;k++){
					C.a[i][j]=inc(C.a[i][j],a[i][k]*B.a[k][j]%MOD);
				}
			}
		}
		return C;
	}
};
Matrix QuickPow(Matrix A,ll y){
	Matrix res;
	res.a[1][1]=res.a[2][2]=1;
	while(y){
		if(y&1) res=res*A;
		A=A*A;
		y>>=1;
	}
	return res;
}
Matrix ans,base,base1;
string nn,mm;
ll a,b,c,d,n,m;
int main(){
	cin>>nn>>mm>>a>>b>>c>>d;
	int len1,len2;
	len1=nn.size(),len2=mm.size(); 
	for(int i=0;i<len1;i++) n=(n*10+nn[i]-'0')%(a==1?MOD:MOD-1);
	for(int i=0;i<len2;i++) m=(m*10+mm[i]-'0')%(a==1?MOD:MOD-1);
	ans.a[1][1]=1,ans.a[1][2]=1;
	base.a[1][1]=a,base.a[2][1]=b,base.a[2][2]=1;
	base=QuickPow(base,m-1);
	base1.a[1][1]=c,base1.a[2][1]=d,base1.a[2][2]=1;
	base=base*base1;
	base=QuickPow(base,n-1);
	base1.a[1][1]=a,base1.a[2][1]=b,base1.a[2][2]=1;
	base1=QuickPow(base1,m-1);
	write((ans*(base*base1)).a[1][1]);
	return 0;
}