洛谷 P2151 [SDOI2009]HH去散步

本文介绍了一种结合动态规划与矩阵快速幂优化的算法解决路径方案数问题的方法。通过节点拆分避免回头路径,利用矩阵快速幂进行状态转移,有效解决了大数值下路径方案数量的计算。

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

题目分析

求方案数显然用动态规划。

首先不考虑沿刚刚走来的路走回。

\(f[i][j]\)表示走距离\(j\)到达\(i\)的方案数。那么转移方程十分显然,不用赘述。注意到\(t\)十分大,很容易想到利用矩阵快速幂优化。

现在考虑不能沿刚刚走来的路走回。我们将每一个点\(a_i\)拆成\(a_{ij}\)
表示从编号为\(j\)的边走来到达\(i\)。这样再转移就没有问题了。
最后将所有的\(a_{Bj}\)的方案数求和就可以得到答案了。

#include<bits/stdc++.h>
using namespace std;
const int mod=45989;
int n,m,t,A,B,sz;
vector<int>v[55];
struct node{int l,r,now;}e[65];
struct Mat{
    int a[125][125];
    Mat(){memset(a,0,sizeof(a));}
    void init(){
        for(int i=0;i<sz;i++)a[i][i]=1;
    }
    int*operator[](int x){return a[x];}
    Mat operator*(Mat &b){
        Mat c;
        for(int i=0;i<sz;i++)
            for(int j=0;j<sz;j++)
                for(int k=0;k<sz;k++)
                    c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j])%mod;
        return c;
    }
    Mat operator^(int k){
        Mat ret,mul=*this;ret.init();
        for(;k;k>>=1,mul=mul*mul)if(k&1)ret=ret*mul;
        return ret;
    }
};
int main(){
    scanf("%d%d%d%d%d",&n,&m,&t,&A,&B);
    sz=0;
    Mat G,Ans;
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        e[i]=(node){x,y,sz};
        v[x].push_back(sz);sz++;
        v[y].push_back(sz);sz++;
        if(x==A)Ans[0][sz-1]++;
        if(y==A)Ans[0][sz-2]++;
    }
    for(int p=1;p<=m;p++){
        int x=e[p].l,y=e[p].r,now=e[p].now;
        for(int i=0;i<v[x].size();i++)if(v[x][i]!=now)G[v[x][i]][now+1]=1;
        for(int i=0;i<v[y].size();i++)if(v[y][i]!=now+1)G[v[y][i]][now]=1;
    }
    G=G^(t-1);Ans=Ans*G;
    int ans=0;
    for(int i=0;i<v[B].size();i++)ans=(ans+Ans[0][v[B][i]])%mod;
    cout<<ans<<"\n";
}

转载于:https://www.cnblogs.com/Trrui/p/9676811.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值