P2151 [SDOI2009]HH去散步
题目描述
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。
现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径
输入输出格式
输入格式:第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。
接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai != Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。
输出格式:一行,表示答案。
输入输出样例
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2
4
说明
对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。
对于100%的数据,N ≤ 50,M ≤ 60,t ≤ 2^30,0 ≤ A,B
Solution:
本题矩阵加速dp。
一眼想到dp,因为刚走过的边不能立即返回,若按点去定义状态就会不好判断重边和刚走过的边的情况,解决办法是按边去定义状态,先把无向边拆成有向边,设$f[i][j]$表示到了第$i$条边走了$j$距离的方案数,于是$f[i][j]=\sum f[k][j-1]$(其中第$k$条边能到第$i$条边,且$i,k$不是同属一条无向边)。
于是就能矩阵优化dp了,初始矩阵就是个$1*2m$的矩阵,其中是$A$的出边都标记为1,然后转移矩阵是$2m*2m$的矩阵,由入边$i$向出边$j$转移,所以使得$matrix[i][j]++$即可。
最后答案就统计到达$B$的入边的方案数之和就好了。
代码:
/*Code by 520 -- 9.11*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) #define Clr(p) memset(&p,0,sizeof(p)) using namespace std; const int mod=45989; int n,m,t,A,B,to[130],net[130],h[130],cnt=1; struct matrix{int a[130][130],r,c;}; il matrix Mul(matrix x,matrix y){ matrix tp; Clr(tp); tp.r=x.r,tp.c=y.c; For(i,0,x.r) For(j,0,y.c) For(k,0,x.c) tp.a[i][j]=(tp.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod; return tp; } il void solve(int k){ matrix tp,ans; Clr(tp),Clr(ans); ans.r=0,ans.c=tp.r=tp.c=cnt; for(RE int i=h[A];i;i=net[i]) ans.a[0][i]=1; For(u,0,n-1) for(RE int i=h[u];i;i=net[i]) { RE int v=to[i]; for(RE int j=h[v];j;j=net[j]) if((j^1)!=i) tp.a[i][j]++; } while(k){ if(k&1) ans=Mul(ans,tp); k>>=1; tp=Mul(tp,tp); } int tot=0; for(RE int i=h[B];i;i=net[i]) tot+=ans.a[0][(i^1)]; cout<<tot%mod; } il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;} int main(){ ios::sync_with_stdio(0); cin>>n>>m>>t>>A>>B; int u,v; For(i,1,m) cin>>u>>v,add(u,v),add(v,u); solve(t-1); return 0; }