洛谷 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";
}