洛谷4159 [SCOI2009] 迷路(矩阵快速幂,拆点)

题意:该有向图有 n 个节点,节点从 1至 n 编号,windy 从节点 1 出发,他必须恰好在 t 时刻到达节点 n。现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?答案对2009取模。(洛谷 4159)

输入格式:第一行包含两个整数,分别代表 n和 t。第 2 到第 (n + 1)行,每行一个长度为 n 的字符串,第 (i + 1)行的第 j 个字符c [ i ] [ j ]是一个数字字符,若为 0,则代表节点 i 到节点 j 无边,否则代表节点 i 到节点 j 的边的长度为c [ i ] [ j ]。

输出格式:输出一行一个整数代表答案对 2009取模的结果。

数据范围: 2 ≤ n ≤ 10  , 1 ≤ t ≤ 1e9。

分析:首先考虑边权只有  $0,1$ 的情况。令  $f_1=$给定矩阵。因为边权只有  $0,1$,所以我们可以将这个矩阵的意义转化成: $f_t[i][j]=k \Longleftrightarrow$  $i$到 $j$的长度为 $t$的路径条数为 $k$。显然有  $f_t[i][j]=\sum\limits_{k=1}^nf_{t-1}[i][k]\times f_1[k][j]$。

矩阵乘法满足结合律,故  $f_t=f_1^t$。于是这种情况下答案就是  $f_T[1][n]$。考虑边权  $w\in [0,9]\cap\mathbb Z$ 的情况。因为边权可能大于1,所以给定矩阵不能直接转换成那种含义了。但是我们发现  $n\le 10$。这意味着我们可以将每个点都拆开,将这张图转化成边权只有  $0,1$ 的图,这样上面的意义就成立了。

我们发现可以将每个点拆成  $9$ 个点,令有序数对  $(i,j)(i\in [1,n]\cap\mathbb Z,j\in [0,8]\cap\mathbb Z)$ 表示点  $i$ 拆成的第  $j$ 个点,其中第  $0$ 个点是“真”点,其余的是“假”点。我们可以令  $(i,j)(j\in [1,8]\cap\mathbb Z)$ 表示到“真”点  $(i,0)$ 的距离为  $j$ 的“假”点,只要让  $(i,j)(j\in [1,8]\cap\mathbb Z)$ 向  $(i,j-1)$ 连一条边权为  $1$ 的边。这样我们就还原了原图中的边,并且将边权都转化成了  $0,1$。而每个  $(i,j)$ 又可以唯一对应一个编号  $i+j\times n$,因此原矩阵就变成了一个  $9n\times 9n$ 的矩阵  $f_1$。根据前面的推理,同样  $f_t=f_1^t$。答案就是  $f_T[1][n]$。

#include<cstdio>
#include<cstring>
const int mod = 2009;

int n,T,m,x;

struct Node{
    int a[100][100];
    Node operator *(const Node &x)const{
        Node ans;
        memset(ans.a,0,sizeof(ans.a));
        for(int i = 1; i <= m; ++i)
            for(int t = 1; t <= m; ++t)
                for(int k = 1; k <= m; ++k)
                    ans.a[i][t] = (ans.a[i][t]+a[i][k]*x.a[k][t]) % mod;
        return ans;
    }
}res,ans;

inline int cal(int x,int y){ return x+y*n; }

void quick_pow(int k){
    ans = res;
    while(k){
        if(k&1)  ans = ans*res;
        res = res*res;  k >>= 1;
    }
}

int main(){
    scanf("%d%d",&n,&T);  m = 9*n;
    for(int i = 1; i <= n; ++i){
        for(int t = 1; t <= 8; ++t)
            res.a[cal(i,t)][cal(i,t-1)] = 1;
        for(int t = 1; t <= n; ++t){
            scanf("%1d",&x);
            if(x)   res.a[i][cal(t,x-1)] = 1;
        }
    }
    quick_pow(T-1);
    printf("%d",ans.a[1][n]);
    return 0;
}

原文链接:https://www.luogu.com.cn/blog/samxiang/solution-p4159

posted @ 2020-11-23 13:59  のNice  阅读(52)  评论(0编辑  收藏  举报