洛谷 P2151 [SDOI2009]HH去散步

题目分析

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

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

\(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";
}
posted @ 2018-09-19 20:12  Trrui  阅读(129)  评论(0编辑  收藏  举报