大致题意: 给定一张无向图,让你从起点出发,每次不能重走上次的边(两点之间可能有重边),问走(k)步到终点的方案数。
矩乘
考虑矩乘。
一开始(naive)想要直接记第(i)行第(j)列为从(i)号点转移到(j)号点的方案数,由于有不能重走上次的边这一限制,无法直接转移。
然后就开始考虑,记一个点对((x,y))表示从第(y)条边走到(x)号点这一状态,然后记第(i)行第(j)列为从第(i)个状态转移到第(j)个状态的方案数,由于方案数总数(nm),直接炸飞。
结果突然发现,确定了(y)自然就确定了(x)。
于是,只要记第(i)行第(j)列为从(i)号边转移到(j)号边的方案数,就可以了。
至于不能重走上次的边,套路地把一条无向边拆成两条有向边,规定一条边拆成的两条边不能相互转移即可。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 120
#define X 45989
#define LL long long
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,k,s,t;struct line {int x,y;}e[N+5];
struct M//矩乘
{
LL a[N+5][N+5];I M() {memset(a,0,sizeof(a));}I LL* operator [] (CI x) {return a[x];}
I M operator * (Con M& o) Con
{
M t;RI i,j,k;for(i=1;i<=m;++i) for(j=1;j<=m;++j)
for(k=1;k<=m;++k) t[i][j]=a[i][k]*o.a[k][j]+t[i][j];
for(i=1;i<=m;++i) for(j=1;j<=m;++j) t[i][j]%=X;return t;
}
I M operator ^ (RI y) Con
{
M x=*this,t;for(RI i=1;i<=m;++i) t[i][i]=1;W(y) y&1&&(t=t*x,0),x=x*x,y>>=1;return t;
}
}U;
int main()
{
RI i,j;for(scanf("%d%d%d%d%d",&n,&m,&k,&s,&t),i=1;i<=m;++i)
scanf("%d%d",&e[i].x,&e[i].y),e[m+i].x=e[i].y,e[m+i].y=e[i].x;m<<=1;//读入边,拆两条
for(i=1;i<=m;++i) for(j=1;j<=m;++j) e[i].y==e[j].x&&abs(i-j)^(m>>1)&&(U[i][j]=1);//注意不能是同一条边拆成的
LL g=0;for(U=U^(k-1),i=1;i<=m;++i) for(j=1;j<=m;++j) e[i].x==s&&e[j].y==t&&(g+=U[i][j]);//根据快速幂后的矩阵计算答案
return printf("%d",g%X),0;//输出答案
}