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]);
}
posted @ 2020-06-19 21:46  小蒟蒻皮皮鱼  阅读(124)  评论(0)    收藏  举报