Loading

题解 【P4159 [SCOI2009] 迷路】

\(\large{\texttt{P4159}}\)

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


\(\large{\texttt{Meaning}}\)

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


\(\large{\texttt{Solution}}\)

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

注意到边权为 \(1\)~\(9\) ,所以可以将每一个点拆成 \(9\) 个点,若原先每个点为 \(i\) ,则拆后的点为 \((i-1)*9+n~(1\le n\le9)\)\(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{\texttt{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{\texttt{My Blog}}\)

posted @ 2020-07-21 20:35  RedreamMer  阅读(54)  评论(0编辑  收藏  举报