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;
}

浙公网安备 33010602011771号