P4159 [SCOI2009] 迷路
P4159 [SCOI2009] 迷路
这道题一个很奇怪的特点就是边权是以字符串的形式给的,也就是说两点之间的距离小于等于9。
不妨先考虑当路径长度都是1的情况,那么题目要求的其实就是走了\(t\)步到达\(n\)点的方案数
设\(f[i][j][t]\)表示从\(i\)到\(j\)恰好走了\(t\)步的方案数,则
\(f[i][j][t] = \sum\limits_{k=1}^nf[i][k][t-1]*f[k][j][1]\)
发现这不正好是矩阵乘法的公式吗。
设\(t\)时刻的矩阵为\(f_t\),则\(f_t=f_{t-1}*f_1\),也就是\(f_t=f_1^t\),直接矩阵快速幂搞一下就好了,而\(f_1\)也就是题中所给的邻接矩阵。
回到原来的问题上,边权小于等于\(9\)怎么做?
有一个想法就是把一个点看成\(9\)个点,在这九个点之间连边。这样就把问题转化成了先前我们讨论的问题。那么接下来考虑的就是如何拆点。不妨先考虑简单的情况,两个点,边权最大为\(2\)。
有如下矩阵:
\[\begin{Bmatrix}
2&1\\
2&0
\end{Bmatrix}
\]
画出图就是这样

拆点后

这里每条边的边权都是1,其中点\(1.1\)和\(2.1\)表示实际的点,\(1.2\)和\(2.2\)表示拆出来的点。
以此类推到九个点也是一样的
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
const int mod = 2009;
int n, t, sz;
struct martix
{
int m[N][N];
}e, a;
martix cheng(martix x, martix y)
{
martix c;
for(int i = 1; i <= sz; i ++)
for(int j = 1; j <= sz; j ++) c.m[i][j] = 0;
for(int i = 1; i <= sz; i ++)
for(int j = 1; j <= sz; j ++)
for(int k = 1; k <= sz; k ++)
c.m[i][j] = (c.m[i][j] + x.m[i][k] * y.m[k][j] % mod) % mod;
return c;
}
martix pow(martix x, int y)
{
martix ans = e;
while(y)
{
if(y & 1) ans = cheng(ans, x);
x = cheng(x, x);
y >>= 1;
}
return ans;
}
char s[N];
int main()
{
scanf("%d%d", &n, &t);
sz = n * 10;
for(int i = 1; i <= sz; i ++)
e.m[i][i] = 1;
for(int i = 1; i <= n; i ++)
{
int now = (i - 1) * 10;
for(int j = 2; j <= 9; j ++)
a.m[j + now - 1][j + now] = 1;
}
for(int i = 1; i <= n; i ++)
{
scanf("%s", s + 1);
int now = (i - 1) * 10;
for(int j = 1; j <= n; j ++)
{
if(s[j] == '0') continue;
int to = s[j] - '0';
a.m[now + 9][j * 10 - to] = 1;
}
}
e = pow(a, t);
printf("%d\n", e.m[9][(n-1) * 10 + 9]);
}

浙公网安备 33010602011771号