1036. 【SCOI2009】迷路 (Standard IO)

 

Time Limits: 1000 ms  Memory Limits: 65536 KB  Detailed Limits  

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 。

Source / Author: 四川2009省选第2试第3题

 

题解:

N那么鬼死小 , T那么鬼死大,肯定是矩乘。

先想出我们的朴素dp , f[k][i][j]表示从i到走到j点刚好到了k时刻的方案数。

因为T很大 , 想到用f乘上(某转移矩阵M) T次  , 这样才不会炸时间。

M是什么呢?

假如边权是1的话 , 对于边 i -> j , M[i][j]=1表示(到当前时间点)i点能走到j点的方案数为1.

然后直接快速幂T次即可。

 

但是边权不一定为1啊 , 这样构造显然是不行的。既然这样 , 我们就把一个点拆成9个点(边权从1到9) ,先把所有(i,k)向(i,k+1)连一条边,对于一条边i->j边权为k , 我们将(i , k) 和(j , 1)连一条1的边 , 也就是M[(i,k)][(j,1)]=1 。

如图:

 

:我们钦定(i,1)为i的起始点 , 先把所有(i,k)向(i,k+1)连一条边。

 

:对于一条边i->j边权为k , 我们将(i , k) 和(j , 1)连一条1的边 , 也就是M[(i,k)][(j,1)]=1 。

(如图是当k等于3的情况)

这样构造的话 从(i,1)到(i,k)一共走了(k-1)条边 , 还有一条从(i,k)到(j,1)的边,一共k条。

 

就是说我们把权值k拆成了k段权值为1的来做。

 

至于矩阵乘法的内部原理 , 不用多解释 , 自己手玩样例即可食用。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mcy(a,b) memcpy(a,b,sizeof(a))
#define ll long long
#define inf 2000000000
#define N 110
#define maxn 150
#define mod 2009
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;

int n,T ,tmp;
char s[N];
int a[N][N] ;
struct matrix
{
	int a[maxn][maxn];
	int L , R;
	matrix() {mem(a,0);L = R  =0;} 
	void init(int l,int r) {mem(a,0);L = l ; R = r;}
	void print()
	{
		for(int i=1;i<=L;i++,puts(""))
			for(int j=1;j<=R;j++) printf("%d " , a[i][j]);
	}
	void read()
	{
		for(int i=1;i<=L;i++)
		{
			for(int j=1;j<=R;j++) scanf("%d" , &a[i][j]);
		}
	}
	
}f , c;
//g i , j i状态转移到j状态的期望 
__attribute((optimize(3)))
matrix mul(matrix a,matrix b)
{
	matrix c;
	c . init(a.L , b.R);
	for(int i=1;i<=a.L;i++)
		for(int j=1;j<=b.L;j++)
			for(int k=1;k<=a.R;k++) (c.a[i][j] += a.a[i][k] * b.a[k][j])%=mod;
	return c;
}
__attribute((optimize(3)))
matrix operator * (matrix a,matrix b)
{return mul(a,b);}
__attribute((optimize(3)))
matrix sqr(matrix x)
{return x*x;}
__attribute((optimize(3)))
matrix power(matrix x , int p)
{
	matrix ans = c;
	while(p)
	{
		if(p&1) ans  = ans * x;
		x = sqr(x);
		p>>=1;
	}
	return ans;
}
__attribute((optimize(3)))
void read()
{
	for(int i=1;i<=n;i++)
	{
		scanf("%s\n",s+1);
		for(int j=1;j<=n;j++) 
			a[i][j] = s[j] - '0' ;
	}
}
int st=0,id[N][10];
__attribute((optimize(3)))
void init()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=9;j++)++st , id[i][j] = st;
	f.init(st,st);
	for(int i=1;i<=n;i++)
		for(int j=1;j<9;j++) f.a[id[i][j]][id[i][j+1]] = 1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) if(tmp = a[i][j]) f.a[id[i][tmp]][id[j][1]] = 1;
	c.init(st,st);
	for(int i=1;i<=st;i++)c.a[i][i]=1;
	f.print();
		 
}
matrix ans;
__attribute((optimize(3)))
void work()
{
	f = power(f,T);
	ans = f;
}

__attribute((optimize(3)))
int main()
{
	open("way");
	scanf("%d%d\n",&n,&T);
	read();
	init();
	work();
	printf("%d",ans . a[id[1][1]][id[n][1]]);
	
	return 0;
}

O(log T *  statetot ^ 3)

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值