题解-P4159 [SCOI2009] 【迷路】

(large{ exttt{P4159}})

题目和普通的 (01) 路径矩阵加速有一点区别,做法很巧。


(large{ exttt{Meaning}})

给定一个邻接矩阵,即每个点之间的边权,若为 (0) 则无边,因为是 (a*a) 的矩阵,所以隐藏含义是每条边权 (1)~(9)


(large{ exttt{Solution}})

因为边权不只是 (1) 了,所以不能直接就将每个点连接边做矩乘,但是数据范围太大又不能不用矩乘。

注意到边权为 (1)~(9) ,所以可以将每一个点拆成 (9) 个点,若原先每个点为 (i) ,则拆后的点为 ((i-1)*9+n~(1le nle9))(i) 的主点为 ((i-1)*9+1) ,并将点 ((i-1)*9+n)((i-1)*9+n+1) 连边长为 (1)

然后连长度不为 (1) 的边就很方便了,若连点 (u)(v) 边权为 (k) ,则连接 ((u-1)*9+k)((v-1)*9+1) ,这样,从 (u) 的主点必须要走 (k-1) 个单位,再走这条边才到的主点,且可以使用矩阵乘法+快速幂。

最后答案即为 (1) 的主点到 (n) 的主点的距离。


(large{ exttt{Code}})

#include<bits/stdc++.h>
using namespace std;

// #define int long long
const int mod=2009;
const int N=10;

int a,b;
char ch;

struct node {
    int n,m,s[1010][1010];
    inline void Mem() {memset(s,0,sizeof(s));}
    inline void Re() {for(int i=1;i<=n;i++) s[i][i]=1;}
    inline void print() {for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) printf("%d ",s[i][j]); puts("");}}
    node operator*(const node b) {
        node sum;
        sum.Mem();
        sum.n=n;
        sum.m=b.m;
        for(int i=1;i<=n;i++) for(int j=1;j<=b.m;j++) {for(int k=1;k<=m;k++)  sum.s[i][j]=(sum.s[i][j]+s[i][k]*b.s[k][j]); sum.s[i][j]%=mod;}
        return sum;
    }
} S,A;

inline void Pow(int n) {//矩阵快速幂
    while(n) {
        if(n&1) S=S*A;
        A=A*A;
        n>>=1;
    }
}

signed main() {
    // freopen("in","r",stdin);
    scanf("%d%d",&a,&b);
    S.n=S.m=a*9;
    for(int i=1;i<=a;i++) for(int j=1;j<=8;j++) S.s[(i-1)*9+j][(i-1)*9+j+1]=1;
    for(int i=1;i<=a;i++) {
        getchar();//防止scanf读入偏差
        for(int j=1;j<=a;j++) {
            scanf("%c",&ch);
            if(ch!='0') S.s[(i-1)*9+ch-'0'][(j-1)*9+1]=1;
        }
    }
    A=S;
    Pow(b-1);
    //S.print();
    printf("%d",S.s[1][(a-1)*9+1]);
    return 0;
}

(large{ exttt{My Blog}})

原文地址:https://www.cnblogs.com/RedreamMer/p/13357250.html