C++图论与矩阵加速—————[SCOI2009]迷路(道路千万条)

题目描述:

道路千万条,安全第一条!宏帆校区到渝北校区有很多种走法,我们可以把走法看成N个节点的有向图,假设宏帆代表0号节点,渝北代表N-1号节点,GM想从0号节点出发,到N-1号节点,但必须恰好在T时刻到达!你能告诉GM一共有多少种走法吗?注意:GM不能在某个节点逗留,且通过某有向边时严格为给定时间(边权)。

输入:

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

满足 2 <= N <= 10 ; 1 <= T <= 1000000000。

输出:

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

输入样例1:

2 2
11
00

输出样例1:

1

输入样例2:

5 30

12045

07105

47805

12024

12345

输出样例2:

852

思路分析:

我们先这样弱化一下题目

假设这个方阵只有0或1,那么其就是不是表示它们点之间的有向边。

而它自己和自己相乘就是表示从一个顶点到另一个顶点。

在T次方之后,矩阵的数就是从i到j的方案数。(其中i为行数,j为列数)。

遗憾的是本题的边权是[0,9],不是[0,1]。

惊喜的是最大边权是9!N也不超过10!都不算大!

马上想到:拆点!把一个点拆成9个点,max:10*9。(当时我连拆点是什么都不知道)。

拆点:

假设有两个节点,边权不超过2的2阶方阵

可以画图为:

由于n和边权都不大,所以想到拆点,把原先的1个点拆成(max边权)个点,所以拆完之后仍然是一个n*(max边权)的方阵

我们先将单链连接起来,毕竟同点。

然后由于一个规则,原图的i,j点是现图的i.1和j.1点,所以它们的边就是从i.1到j.1的路了。

那么原i到j的路程就是先从i.1到i.边权-1到2.1了。

具体拆分如下:

我们把1.1看成节点1;把1.2看成节点2;把2.1看成节点3;把2.2看成节点4;可得到新矩阵 :

这样我们就可以求出答案。

而新矩阵的行列就是N=n*maxn(maxn为最大边)。

最后一点,答案为B[1][N-maxn+1],这一点就自行思考(实在写不下去了)。

代码实现;

#include<cstdio>
#include<iostream>
#include<cstring>
#define mod 2009
using namespace std;
int n,m,t,maxn;
char a[15][15];
struct node{
    int n,m,c[105][105];
    node ()
    {
        memset(c,0,sizeof(c));
    }
    node operator*(const node &a)
    {
        node r;
        r.n=n;
        r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+(c[i][k]*a.c[k][j])%mod)%mod;
        return r;
    }
};
node qkpow(node a,int p)
{
    node res;
    res.n=res.m=n;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;
    while(p)
    {
        if(p&1)
            res=res*a;
        a=a*a;
        p/=2;
    }
    return res;
}
int js(int x,int y)
{
    return (x-1)*maxn+y;
}
int main()
{
    scanf("%d%d",&n,&t);
    for(int i=1;i<=n;i++)
    {
        scanf("\n");
        for(int j=1;j<=n;j++)
        {
            scanf("%c",&a[i][j]);
            maxn=max(a[i][j]-'0',maxn);
        }
    }
    node b;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<maxn;j++)
            b.c[js(i,j)][js(i,j+1)]=1;
        for(int j=1;j<=n;j++)
            if(a[i][j]!='0')
                b.c[js(i,a[i][j]-'0')][js(j,1)]=1;
    }
    n*=maxn;
    b.n=n;
    b.m=n;
    b=qkpow(b,t);
    printf("%d",b.c[1][n-maxn+1]);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章