链接
https://www.luogu.org/problem/show?pid=2151
题解
对于30%:
可以令f[i][j][k]表示走了i步到达j这个点,前驱边是k的方案数。
那么转移f[i][j][k]->f[i+1][从i能到达的点][这条边不是k的反向边]。
复杂度O(tNM^2)。
对于100%:
看到这种转移的形式很容易想到用矩阵乘法加速,那么t这一维就可以变成log(t),但是状态数仍然是NM,也就是说矩阵的大小是N*M,那么做一次矩阵乘法就是(NM)^3,显然太大。
但是知道了边就等于知道了这是哪个点,因此N可以被去掉,矩阵大小变成M*M,这样的话复杂度变成了O(M^3*log(t)),可以。
代码
//动态规划+矩阵乘法
#include <cstdio>
#include <algorithm>
#define mod 45989
#define maxn 25
#define maxm 65
#define maxt 250
using namespace std;
int head[maxm<<1], to[maxm<<1], next[maxm<<1], tot=1, N, M, T, A, B, f[2][maxm<<1],
trans[maxm<<1][maxm<<1];
void adde(int a, int b){to[++tot]=b;next[tot]=head[a];head[a]=tot;}
void cpy(int a[][maxm<<1], int b[][maxm<<1])
{
int i, j;
a[0][0]=b[0][0], a[0][1]=b[0][1];
for(i=1;i<=a[0][0];i++)for(j=1;j<=a[0][1];j++)a[i][j]=b[i][j];
}
void mult(int a[][maxm<<1], int b[][maxm<<1], int c[][maxm<<1])
{
int x=a[0][1], i, j, k, t[maxm<<1][maxm<<1]={0};
t[0][0]=a[0][0], t[0][1]=b[0][1];
for(i=1;i<=t[0][0];i++)for(j=1;j<=t[0][1];j++)for(k=1;k<=x;k++)
t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;
cpy(c,t);
}
void pow(int a[][maxm<<1], int b, int c[][maxm<<1])
{
int ans[maxm<<1][maxm<<1], t[maxm<<1][maxm<<1];
ans[0][0]=a[0][0], ans[0][1]=a[0][1];
for(int i=1;i<=ans[0][0];i++)ans[i][i]=1;
for(cpy(t,a);b;b>>=1,mult(t,t,t))if(b&1)mult(ans,t,ans);
cpy(c,ans);
}
void init()
{
int u, v, i, p;
scanf("%d%d%d%d%d",&N,&M,&T,&A,&B);
for(i=1;i<=M;i++)
{
scanf("%d%d",&u,&v);
adde(u,v),adde(v,u);
}
f[0][0]=1,f[0][1]=tot;
for(p=head[A];p;p=next[p])f[1][p]=1;
trans[0][0]=trans[0][1]=tot;
for(i=2;i<=tot;i++)
for(p=head[to[i]];p;p=next[p])if(p!=(i xor 1))trans[i][p]=1;
}
int main()
{
int ans=0, i;
init();
pow(trans,T-1,trans);
mult(f,trans,f);
for(i=2;i<=tot;i++)if(to[i]==B)ans=(ans+f[1][i])%mod;
printf("%d",ans);
return 0;
}