Description
机器人初始在 1 1 1 号城市上。机器人有三种行为:停在原地、下一个相邻的城市、自爆。它每一秒都会随机触发一种行为。现在给出图,问经过了 t t t 秒, 机器人的行为方案数是多少?
1 < t ≤ 1 0 9 , 1 ≤ N ≤ 30 , 0 < M < 100 1<t \leq 10^{9}, 1 \leq N \leq 30,0<M<100 1<t≤109,1≤N≤30,0<M<100
Solution
地图很小,先建出邻接矩阵。对于停在原地,让 f i , i = 1 f_{i,i} = 1 fi,i=1,对于自爆,可以建一个虚拟节点 0 0 0,然后让所有的点都向它连一条有向边,即为 f i , 0 = 1 f_{i,0} = 1 fi,0=1。
考虑 dp 的过程,设 g i , j , k g_{i,j,k} gi,j,k 为从 i i i 到 j j j 走了 k k k 步的方案数,可以发现 k k k 这一维可以滚动掉。那么初始状态即为 g i , j = f i , j g_{i,j} = f_{i,j} gi,j=fi,j。运用 Floyd 的思想,转移为
f i , j = ∑ k = 1 n f i , k × f k , j f_{i,j} = \sum_{k=1}^n f_{i,k} \times f_{k,j} fi,j=k=1∑nfi,k×fk,j
可以发现这是矩阵乘法,所以矩阵快速幂搞定。
时间复杂度 O ( n 3 log t ) O(n^3 \log t) O(n3logt)。
Code
挂个矩阵快速幂的模板
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100 + 5, INF = 0x3f3f3f3f, mod = 1e9 + 7;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
int n;
struct mat {
int m[N][N];
mat() {
memset(m, 0, sizeof(m));
for (int i = 0; i < N; i++) m[i][i] = 1;
}
};
mat mul(mat a, mat b) {
mat c;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
c.m[i][j] = 0; //Mention!
for (int k = 1; k <= n; k++)
c.m[i][j] += a.m[i][k] * b.m[k][j] % mod;
c.m[i][j] %= mod;
}
return c;
}
mat ksm(mat a, int k) {
mat res;
while (k) {
if (k & 1) res = mul(res, a);
k >>= 1;
a = mul(a, a);
}
return res;
}
signed main() {
n = read(); int k = read();
mat a;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a.m[i][j] = read();
mat ans = ksm(a, k);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
printf("%lld ", ans.m[i][j]);
puts("");
}
return 0;
}