SCOI2009 迷路 题解

---恢复内容开始---

题目描述

Windy 在有向图中迷路了。 该有向图有 NNN 个节点,Windy 从节点 000 出发,他必须恰好在 TTT 时刻到达节点 N−1N-1N1。

现在给出该有向图,你能告诉 Windy 总共有多少种不同的路径吗?

注意:Windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

输入格式

第一行包含两个整数,N,TN,TN,T;
接下来有 NNN 行,每行一个长度为 NNN 的字符串。第 iii 行第 jjj 列为 0 表示从节点 iii 到节点 jjj 没有边,为 19 表示从节点 iii 到节点 jjj 需要耗费的时间。

输出格式

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

样例

样例输入 1

2 2
11
00

样例输出 1

1

样例说明 1

0→0→10\to 0\to 1001

样例输入 2

5 30
12045
07105
47805
12024
12345

样例输出 2

852

数据范围与提示

对于 30%的数据,满足 2≤N≤5,1≤T≤30
对于 100%的数据,满足 2≤N≤10,1≤T≤10^9……9​​。

   思路:

    这题可以说是一种定理题,在矩阵中,一个存i到j路径数的矩阵自乘k次,a[i][j]表示从i到j走k步的路径数。我们来证明一下,其实仔细理解一下矩阵乘,c[i][j]+=a[i][k]*b[k][j],一写出来就明朗了,枚举一个中间点,用乘法原理,i到j两步的路径数等于i到k一步的路径数乘k到j一步的路径,然后再对于不同的中间点用加法原理,这一定是正确并且全面的,每次都枚举了i能到的所有点,以及能到j的所有点,k步就显然一样。然后会发现这道题并没有被解决,因为他的边权可能为1~9,如果直接存进矩阵的话,存边权显然是不可取的,它表示有边权数条路径,存1好像成了一步就能到。正解是把一个点拆成9个点,可以认为搞一个9进制,然后进行连边,具体实现不好叙述,大概是这样

    for(int i=1;i<d;i++)
        a.a[x*9+i][x*9+i+1]=1;
    a.a[x*9+d][y*9+1]=1;

  然后就可以保证边权是k的时候能够正确处理。

  

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
const int mod=2009;
struct node{ll a[102][102];}a,ans;
ll n,t;
void cheng(node a,node b,node &c)
{
    for(int i=1;i<=n*9;i++)
    for(int j=1;j<=n*9;j++)
    {
        c.a[i][j]=0;
        for(int l=1;l<=n*9;l++)
        c.a[i][j]=(c.a[i][j]+(a.a[i][l]*b.a[l][j]))%mod;
    }
}
void qpow(node a,ll b)
{
    
    while(b)
    {
        if(b&1) cheng(ans,a,ans);
        cheng(a,a,a);
        b>>=1;
    }
}
void add(int x,int y,int d)
{
    if(d==0) return ;
    for(int i=1;i<d;i++)
        a.a[x*9+i][x*9+i+1]=1;
    //    cout<<x*9+i<<" "<<x*9+i+1<<" "<<a.a[x*9+i][x*9+i+1]<<endl;
    a.a[x*9+d][y*9+1]=1;
}
int main()
{
    int d;
    cin>>n>>t;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            scanf("%1d",&d);
            //cout<<d<<" ";
            add(i,j,d);
        }
    ans=a;
    qpow(a,t-1);
//    for(int i=1;i<=(n-1)*9+1;i++)
//    {
//        for(int j=1;j<=(n-1)*9+1;j++)
//        cout<<ans.a[i][j]<<" ";
//        cout<<endl;
//    }
    cout<<ans.a[0*9+1][(n-1)*9+1];
    
}
丑的要死的没有重载运算符的代码

 

 

posted @ 2019-07-18 06:25  starsing  阅读(364)  评论(0编辑  收藏  举报