BZOJ1297 || 洛谷 P4159 [SCOI2009]迷路【矩阵优化DP】

Time Limit: 10 Sec
Memory Limit: 162 MB

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的余数。

HINT

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


题目分析

如果边权只有1的话就是非常简单的矩阵dp
d p [ i ] [ u ] dp[i][u] dp[i][u]表示恰好在i时刻到达u的方案数
d p [ i ] [ u ] = ∑ d p [ i ] [ v ] dp[i][u]=\sum dp[i][v] dp[i][u]=dp[i][v](存在 v → u v\rightarrow u vu的有向边)

考虑边权不一定为1的情况
注意到边权只有1~9
所以直接把每个点拆成9个点
对于边权为 i i i u → v u\rightarrow v uv的有向边
将其转化为边权为1的 u i → v 1 u_i\rightarrow v_1 uiv1的有向边
u拆点后的第i个点v的第一个点

初始化 d p [ 0 ] [ 1 1 ] = 1 dp[0][1_1]=1 dp[0][11]=1
最后 a n s = d p [ t ] [ n 1 ] ans=dp[t][n_1] ans=dp[t][n1]
由于递推方程是线性的,而且t很大,所以矩阵优化DP


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
 
lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}
 
const int mod=2009;
const int maxn=150;
lt n,t;
char ss[maxn];
 
struct matrix
{
	lt mat[maxn][maxn],row,col;
	matrix(int r=0,int c=0){ 
		row=r; col=c; 
		for(int i=1;i<=row;++i)
		for(int j=1;j<=col;++j)
		mat[i][j]=0;
	}
};

matrix operator *(matrix a,matrix b){ 
	matrix c=matrix(a.row,b.col);
	for(int i=1;i<=a.row;++i)
	for(int j=1;j<=b.col;++j)
	for(int k=1;k<=a.col;++k)
	{
		c.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%mod;
		c.mat[i][j]%=mod;
	}
	return c;
}

matrix qpow(matrix a,lt k)
{
	matrix res=matrix(a.row,a.col);
	for(int i=1;i<=a.row;++i) res.mat[i][i]=1;
	while(k){
		if(k&1) res=res*a;
		a=a*a; k>>=1;
	}
	return res;
}

int main()
{
    n=read();t=read();
    
    matrix h=matrix(9*n,9*n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",ss+1);
        for(int j=1;j<=n;++j)
        {
            if(ss[j]=='0') continue;
            else h.mat[(i-1)*9+ss[j]-'0'][(j-1)*9+1]=1;
        }
    }
    
    for(int i=1;i<=n;++i)
    for(int j=1;j<=8;++j)
    h.mat[(i-1)*9+j][(i-1)*9+j+1]=1;
     
    matrix f=matrix(1,9*n); f.mat[1][1]=1;
    matrix res=f*qpow(h,t);
     
    printf("%d",res.mat[1][(n-1)*9+1]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值