[BZOJ1898][Zjoi2005]Swamp 沼泽鳄鱼(dp+矩阵优化)

2/10/2017来源:ASP.NET技巧人气:782

题目描述

传送门

题解

f(i,j)表示时间为i时到达j的方案数,答案即为f(k,t) condi(i,j)表示时间为i时j有没有鳄鱼0/1 那么f(i,j)=∑(v,j)∈Ef(i−1,v) 并且当condi(i,j)=1时f(i,j)=0 时间复杂度O(kn2),考虑怎么优化 因为鳄鱼的周期只可能是234,所以f的转移是以12为周期循环的 那么我们可以把这12次转移用矩阵处理出来,由于矩阵满足结合律,可以先将它们乘起来然后快速幂k/12次,最后在乘上剩余的 矩阵的构造方法就是一个n*n的矩阵,如果f(i-1,j)可以转移到f(i,v),那么在a[j,v]置1

ljw学姐的测试题,完完全全想出来了正解,然而被埋在了一个奇怪的地方——long long不能做位运算! GG

代码

#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define N 55 #define Mod 10000 int n,m,s,t,k,x,y,nfish; int T[N],p[N][5]; bool condi[N][N],flag[N][N]; struct data{int a[N][N];}unit,st,A,trans[N],ans; data cheng(data a,data b) { data ans;memset(ans.a,0,sizeof(ans.a)); for (int k=1;k<=n;++k) for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j])%Mod; return ans; } data fast_pow(data a,int p) { data ans=unit; for (;p;p>>=1,a=cheng(a,a)) if (p&1) ans=cheng(ans,a); return ans; } int main() { scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);++s,++t; for (int i=1;i<=m;++i) { scanf("%d%d",&x,&y);++x,++y; flag[x][y]=flag[y][x]=1; } scanf("%d",&nfish); for (int i=1;i<=nfish;++i) { scanf("%d",&T[i]); for (int j=1;j<=T[i];++j) scanf("%d",&p[i][j]),++p[i][j]; } for (int i=1;i<=min(k,12);++i) for (int j=1;j<=nfish;++j) { int now=p[j][i%T[j]+1]; condi[i][now]=1; } for (int i=1;i<=n;++i) unit.a[i][i]=1; st.a[1][s]=1; for (int i=1;i<=min(k,12);++i) { for (int j=1;j<=n;++j) if (!condi[i][j]) for (int l=1;l<=n;++l) if (flag[l][j]) trans[i].a[l][j]=1; } A=unit; for (int i=1;i<=min(k,12);++i) A=cheng(A,trans[i]); if (k>12) { A=fast_pow(A,k/12); for (int i=1;i<=k%12;++i) A=cheng(A,trans[i]); } ans=cheng(st,A); PRintf("%d\n",ans.a[1][t]); }