1036. 【SCOI2009】迷路

Description

windy在有向图中迷路了。
该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

Input

第一行包含两个整数,N T。
接下来有 N 行,每行一个长度为 N 的字符串。
第i行第j列为'0'表示从节点i到节点j没有边。
为'1'到'9'表示从节点i到节点j需要耗费的时间。

Output

输出一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

Sample Input

2 2
11
00

Sample Output

1

Data Constraint

Hint

100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。

Solution

对于比较小的n,我们可以直接设f[i][j][k]表示从i走到j用了k步转移只需f[i][j][k]=f[i][l][k-1]*f[l][j][k-1]即可,但是这里步数很大,而点数很小,怎么办?

我们很容易想到用矩阵乘法优化这个转移,我们知道可以用一个二维矩阵表示i到j能否用1的时间走到,那我们将这个矩阵自己相乘k次就可以得到第k秒的方案数,但是这题i到j有些不只是1秒,所以每次矩阵相乘的时候不能更新到下一秒,因此我们要将时间转化为0/1矩阵,我们只要将每个点拆成9个点,然后两两连边,对于i,拆成(i-1)*9+1~(i-1)*9+9对于i到j需要x秒的路径,我们可以转化为从i走到复制的点中的第x-1个向j的1号点连一条时间为1的边,即a[(i-1)*9+x][(j-1)*9+1]=1,然后用初始矩阵(f[i][i]=1)乘以k次转移矩阵就可以得到答案(f[1][(n-1)*9+1])了。

Code

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#define I int
#define mem(a,b) memset(a,b,sizeof(a))
#define F(i,a,b) for(I i=a;i<=b;i++)
#define N 100
#define M 2009
using namespace std;
I t,n,a[N][N],f[N][N],g[N][N];
void rd(I &x){
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void mul(){
	mem(g,0);
	F(i,1,n*9)
		F(j,1,n*9)
			F(k,1,n*9){
				g[i][j]=(g[i][j]+f[i][k]*a[k][j])%M;
			}
	F(i,1,n*9){
		F(j,1,n*9) f[i][j]=g[i][j];
	}
}
void tim(){
	mem(g,0);
	F(i,1,n*9)
		F(j,1,n*9)
			F(k,1,n*9){
				g[i][j]=(g[i][j]+a[i][k]*a[k][j])%M;
			}
	F(i,1,n*9){
		F(j,1,n*9) a[i][j]=g[i][j];
	}
}
I main(){
	rd(n),rd(t);
	mem(a,0);
	F(i,1,n*9) f[i][i]=1;
	F(i,1,n){
		I x=(i-1)*9;
		F(j,1,8) a[x+j][x+j+1]=1;
	}
	F(i,1,n){
		F(j,1,n){
			char ch=getchar();
			while(ch<'0'||ch>'9') ch=getchar();
			I x=ch-'0';
			if(x) a[(i-1)*9+x][(j-1)*9+1]=1;
		}
	}
	while(t){
		if(t&1) mul();
		tim();
		t>>=1;
	}
	printf("%d\n",f[1][(n-1)*9+1]);
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/98994108

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值