【[Offer收割]程式設計練習賽13 D】騎士遊歷(矩陣快速冪模板)
阿新 • • 發佈:2019-02-18
題意:
描述
在8x8的國際象棋棋盤上給定一隻騎士(俗稱“馬”)棋子的位置(R, C),小Hi想知道從(R, C)開始移動N步一共有多少種不同的走法。
輸入
第一行包含三個整數,N,R和C。
對於40%的資料, 1 <= N <= 1000000
對於100%的資料, 1 <= N <= 1000000000 1 <= R, C <= 8
輸出
從(R, C)開始走N步有多少種不同的走法。由於答案可能非常大,你只需要輸出答案模1000000007的餘數。
分析:
對於棋盤中任意一個點(R,C),都有多個位置可跳。
比如:(R,C)可以跳到(R+1,C+2)的位置。
為了描述這種關係,可以建立一個64x64的鄰接矩陣;這個鄰接矩陣第一維對應8X8中的每一個點,第二維維護第一維的點可跳的位置。
對於這樣的一個矩陣,用起始矩陣1X64乘以鄰接矩陣64X64,得到的一個1X64的矩陣,這個矩陣的點上值表示從起始位置走一步,到達該位置有多少中方案。(參考離散數學和線性代數)
因此可以將這個乘以矩陣的過程轉化為矩陣快速冪,迅速求得結果。
程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef vector<unsigned long long> vec;
typedef vector<vec> mat;
const unsigned long long mod = 1000000007;
int dir[8 ][2] = { {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1} };
mat mul(mat &a, mat &b)
{
mat c(a.size(), vec(b[0].size(), 0));
for(int i=0; i<a.size(); ++ i)
{
for(int j=0; j<b[0].size(); ++ j)
{
for(int k=0; k<b.size(); ++ k)
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % mod;
}
}
return c;
}
mat pow(mat &a, unsigned long long n)
{
mat b(a.size(), vec(a[0].size(), 0));
for(int i=0; i<a.size(); ++ i)
b[i][i] = 1;
while(n > 0)
{
if(n & 1)
b = mul(b, a);
a = mul(a, a);
n >>= 1;
}
return b;
}
int main()
{
int n, r, c;
cin >> n >> r >> c;
mat a(64, vec(64, 0));
for(int i=0; i<8; ++ i)
{
for(int j=0; j<8; ++ j)
{
for(int k=0; k<8; ++ k)
{
int x = i + dir[k][0], y = j + dir[k][1];
if((x >= 0 && x < 8 && y >= 0 && y < 8) == false)
continue;
int u = x * 8 + y, v = i * 8 + j;
a[u][v] = a[v][u] = 1;
}
}
}
mat b(1, vec(64, 0));
b[0][(r-1)*8 + c-1] = 1;
mat s = pow(a, n);
mat d = mul(b, s);
unsigned long long ans = 0;
for(auto &i: d)
{
for(auto &j: i)
ans = (ans + j) % mod;
}
cout << ans << endl;
return 0;
}