P4159 [SCOI2009] 迷路

P4159 [SCOI2009] 迷路

这道题一个很奇怪的特点就是边权是以字符串的形式给的,也就是说两点之间的距离小于等于9。

不妨先考虑当路径长度都是1的情况,那么题目要求的其实就是走了(t)步到达(n)点的方案数

(f[i][j][t])表示从(i)(j)恰好走了(t)步的方案数,则

(f[i][j][t] = sumlimits_{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)

有如下矩阵:

[egin{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
", e.m[9][(n-1) * 10 + 9]);
}
原文地址:https://www.cnblogs.com/lcezych/p/13166589.html