P4159 [SCOI2009] 迷路

该博客介绍了如何解决一个关于有向图的问题,其中图的节点数为n,边数为m,每条边的权重不超过9。目标是计算从节点1到节点n长度为t的路径数量,结果对2009取模。解决方案包括将边权重为1的情况转化为矩阵乘法问题,并通过矩阵快速幂来处理带权重的边。最后,给出了C++代码实现来解决这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

给一个\(n\)个节点\(m\)条边的带权有向图。求从\(1\to n\)的长度为\(t\)的路径条数。对\(2009\)取模。\(2 \le n \le 10,1 \le t \le 10^9\)
值得一提的是,此题的输入格式:
说明边权\(\le 9\)

Solution

Thinking 1

考虑边权都为\(1\)怎么做。
\(f[t][i][j]\)为长度为\(t\)\(i \to j\)的路径数。
易得:

\[f[t][i][j] = \sum_{k = 1} ^ n f[t - 1][i][k] \cdot f[1][k][j] \]

这就是矩阵乘法板子。易得\(f[t] = f[1]^t\)

Thinking 2

考虑一条\(u \to v\),长度为\(w\)的边。
可以把它拆成\(w\)条长度为\(1\)的边,然后中间用拆的点表示。
然后变成\(10n \cdot 10n\)的矩阵搞。

# include <bits/stdc++.h>
using namespace std;
const int mod = 2009;
int n,t;
struct Matrix
{
    int a[105][105];
    void init(int x = 0)
    {
        for(int i = 1; i <= n * 10; i++)
        {
            for(int j = 1; j <= n * 10; j++) 
            {
                a[i][j] = (i == j) ? x : 0;
            }
        }
        return;
    }
}A;
int calc(int i,int j) {return (i - 1) * 10 + j;}
Matrix operator * (const struct Matrix &x,const struct Matrix &y)
{
    Matrix ans; ans.init();
    for(int i = 1; i <= n * 10; i++)
    {
        for(int j = 1; j <= n * 10; j++)
        {
            for(int k = 1; k <= n * 10; k++)
            {
                ans.a[i][j] = (ans.a[i][j] + x.a[i][k] * y.a[k][j]) % mod;
            }
        }
    }
    return ans;
}
Matrix qpow(Matrix x,int p)
{
    Matrix ans; ans.init(1);
    while(p)
    {
        if(p & 1) ans = ans * x;
        p >>= 1;
        x = x * x;
    }
    return ans;
}
int main(void)
{
    scanf("%d%d",&n,&t);
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= 9; j++)
        {
            A.a[calc(i,j)][calc(i,j + 1)] = 1;
        }
    }
    for(int i = 1; i <= n; i++)
    {
        char s[12];
        scanf("%s", s + 1);
        for(int j = 1; j <= n; j++)
        {
            int x = s[j] - '0';
            if(!x) continue;
            A.a[calc(i,x)][calc(j,1)] = 1;
        }
    }
    Matrix ans = qpow(A,t);
    printf("%d\n",ans.a[1][calc(n,1)]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值