zoukankan      html  css  js  c++  java
  • 洛谷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]=sumlimits_{k=1}^nf_{t-1}[i][k] imes f_1[k][j]$。

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

    我们发现可以将每个点拆成  $9$ 个点,令有序数对  $(i,j)(iin [1,n]capmathbb Z,jin [0,8]capmathbb Z)$ 表示点  $i$ 拆成的第  $j$ 个点,其中第  $0$ 个点是“真”点,其余的是“假”点。我们可以令  $(i,j)(jin [1,8]capmathbb Z)$ 表示到“真”点  $(i,0)$ 的距离为  $j$ 的“假”点,只要让  $(i,j)(jin [1,8]capmathbb Z)$ 向  $(i,j-1)$ 连一条边权为  $1$ 的边。这样我们就还原了原图中的边,并且将边权都转化成了  $0,1$。而每个  $(i,j)$ 又可以唯一对应一个编号  $i+j imes n$,因此原矩阵就变成了一个  $9n imes 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

    你只有十分努力,才能看上去毫不费力。
  • 相关阅读:
    [CQOI2005]三角形面积并(计算几何+扫描线)
    第一天
    LA3026 周期 (kmp)
    HDU 1715 大菲波数 (java大数)
    根据身高重建队列(vector)
    K 连续位的最小翻转次数
    724. Find Pivot Index
    Two Sum
    c
    Most Powerful(状压DP水题)
  • 原文地址:https://www.cnblogs.com/214txdy/p/14024088.html
Copyright © 2011-2022 走看看