BZOJ 1875: [SDOI2009]HH去散步
1875: [SDOI2009]HH去散步
Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 1638 Solved: 791
[Submit][Status][Discuss]
Description
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径
Input
第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。 接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai = Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。
Output
一行,表示答案。
Sample Input
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2
0 1
0 2
0 3
2 1
3 2
Sample Output
4
HINT
对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。 对于100%的数据,N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B
Source
分析:
不错的转化...
我们第一想法大概就是矩阵优化DP,但是发现貌似过不了,所以我们可以考虑用边作为状态来DP...
$f[i][j]$代表从边$i$走向边$j$的方案数...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=120+5,mod=45989; int n,m,t,A,B,ans,cnt,hd[maxn],to[maxn],pre[maxn],nxt[maxn]; struct M{ int a[maxn][maxn]; inline void init(void){ memset(a,0,sizeof(a)); } inline void initone(void){ memset(a,0,sizeof(a)); for(int i=0;i<cnt;i++) a[i][i]=1; } friend M operator * (M x,M y){ M res;res.init(); for(int i=0;i<cnt;i++) for(int j=0;j<cnt;j++) for(int k=0;k<cnt;k++) (res.a[i][j]+=1LL*x.a[i][k]*y.a[k][j]%mod)%=mod; return res; } }pri; inline M power(M x,int y){ M res;res.initone(); while(y){ if(y&1) res=res*x; x=x*x,y>>=1; } return res; } inline void add(int x,int y){ to[cnt]=y;pre[cnt]=x;nxt[cnt]=hd[x];hd[x]=cnt++; } signed main(void){ scanf("%d%d%d%d%d",&n,&m,&t,&A,&B); cnt=0;pri.init();memset(hd,-1,sizeof(hd)); if(t==0) return puts(A==B?"1":"0"),0; for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); for(int i=0;i<cnt;i++) for(int j=0;j<cnt;j++) if(i!=j&&(i^j)!=1&&to[i]==pre[j]) pri.a[i][j]++; pri=power(pri,t-1); for(int i=hd[A];i!=-1;i=nxt[i]) for(int j=hd[B];j!=-1;j=nxt[j]) ans=(ans+pri.a[i][j^1])%mod; printf("%d\n",ans); return 0; }
By NeighThorn