BZOJ1898 [Zjoi2005]Swamp 沼泽鳄鱼 矩阵
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1898
题意概括
有一个无向图。
其中,有许多条鱼在以循环的规律出现,比如循环在1,2,3这些点出现。循环节长度=2,3,4 。
现在,你要从A花费K个单位时间到达B,中途不能和鱼相碰,问有多少方案。
(每个单位时间,鱼从当前的点走向循环中的下一个点)。
n<=50,K<=2000000000
题解
注意到循环节长度为2或3或4.
如果不考虑鱼,那么就是简单的矩阵优化路径统计。可以看这个
现在考虑鱼。
那么就是对于某一时刻,某些鱼所在的位置的路径数都要清0 。
我们发现循环接长度很小。最小公倍数为12!
所以我们可以12个12个来。
对于其中12个,我们大力dp。
然后对于K/12,我们可以用矩阵快速幂解决。对于剩余的K%12,我们也可以再乘上一个矩阵。
然后就搞定了。
代码
#include <cstring> #include <cstdio> #include <cmath> #include <cstdlib> #include <algorithm> using namespace std; const int N=50+5,M=N*N/2,F=20+5,T=5,LCM=12,mod=10000; int n,m,st,en,K,ways[LCM+3][N][N]; bool loc[N][LCM+3],g[N][N]; struct Mat{ int v[N][N]; void set(int x){ memset(v,0,sizeof v); if (x!=1) return; for (int i=1;i<=n;i++) v[i][i]=1; } Mat operator * (Mat x){ Mat ans; ans.set(0); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int k=1;k<=n;k++) ans.v[i][j]=(ans.v[i][j]+v[i][k]*x.v[k][j])%mod; return ans; } }M0,M1,M2,M3; Mat MatPow(Mat x,int y){ Mat ans,now=x; ans.set(1); while (y){ if (y&1) ans=ans*now; now=now*now; y>>=1; } return ans; } void Get_Loc(){ int Nfish,t,a[5]; scanf("%d",&Nfish); memset(loc,0,sizeof loc); while (Nfish--){ scanf("%d",&t); for (int i=1;i<=t;i++) scanf("%d",&a[i]),a[i]++; for (int i=1;i<=12;i++) loc[a[i%t+1]][i]=1; } } int main(){ scanf("%d%d%d%d%d",&n,&m,&st,&en,&K),st++,en++; memset(g,0,sizeof g); for (int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b),a++,b++; g[a][b]=g[b][a]=1; } Get_Loc(); for (int i=1;i<=n;i++) ways[0][i][i]=1; for (int t=1;t<=12;t++){ for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int k=1;k<=n;k++) if (g[j][k]) ways[t][i][k]=(ways[t][i][k]+ways[t-1][i][j])%mod; for (int i=1;i<=n;i++) if (loc[i][t]) for (int j=1;j<=n;j++) ways[t][j][i]=0; } M0.set(0),M2.set(0); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) M0.v[i][j]=ways[12][i][j],M2.v[i][j]=ways[K%12][i][j]; M1=MatPow(M0,K/12); M3=M1*M2; printf("%d",M3.v[st][en]); return 0; }