思路
如果没有不能走上一条边的限制,很显然就是dp。
设f[i][j]表示到达i点走了j步的方案数,移到k点可以表示为f[k][j+1]+=f[i][j]。
如果有限制的话,可以考虑用边表示将之前思路中的i变为边的终点,只要不走同一条边,转移还是相同的。
但是t ≤ 2^30,显然直接dp是不可行的,这是机智的题解就想到了矩阵优化。
由于递推关系是线性的,可以搞一个行矩阵表示对于当前移动步数各个边的方案数。
转移可以考虑构造另一个矩阵,所有可以更新的边之间都变为1,其他是0,对于每一次转移都是一样的,所以只要将矩阵跑t次就可以了。
对于初始矩阵,可以加一条边设一个不存在的点,将它与起点相连,也方便之后的领接表。
此题疯狂卡时,辣鸡出题人。(据说多交几次就可以过
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 const int mod=45989; 6 int n,m,t,e,s,cnt,hd[125],sum; 7 struct edge 8 { 9 int to,from,nxt; 10 }v[125]; 11 struct mat 12 { 13 int a[125][125]; 14 mat() 15 { 16 memset(a,0,sizeof(a)); 17 } 18 mat operator * (mat x) 19 { 20 mat ans; 21 for(int i=1;i<=cnt;i++) 22 for(int j=1;j<=cnt;j++) 23 for(int k=1;k<=cnt;k++) 24 ans.a[i][j]=(ans.a[i][j]+a[i][k]*x.a[k][j])%mod; 25 return ans; 26 } 27 }c,ans; 28 void addedge(int x,int y) 29 { 30 v[++cnt].nxt=hd[x]; 31 v[cnt].from=x; 32 v[cnt].to=y; 33 hd[x]=cnt; 34 } 35 mat mul(mat x,int k) 36 { 37 mat res; 38 for(int i=1;i<=cnt;i++) 39 res.a[i][i]=1; 40 for(int i=k;i;i>>=1,x=x*x) 41 if(i&1) 42 res=res*x; 43 return res; 44 } 45 int main() 46 { 47 scanf("%d%d%d%d%d",&n,&m,&t,&s,&e); 48 s++,e++; 49 addedge(0,s); 50 for(int i=1;i<=m;i++) 51 { 52 int x,y; 53 scanf("%d%d",&x,&y); 54 x++,y++; 55 addedge(x,y),addedge(y,x); 56 } 57 for(int i=1;i<=cnt;i++) 58 for(int j=1;j<=cnt;j++) 59 if((i^j)!=1&&v[i].to==v[j].from) 60 c.a[i][j]=1; 61 ans.a[1][1]=1; 62 c=mul(c,t); 63 ans=ans*c; 64 for(int i=1;i<=cnt;i++) 65 if(v[i].to==e) 66 sum=(sum+ans.a[1][i])%mod; 67 printf("%d ",sum); 68 return 0; 69 }