沼泽鳄鱼
题目链接:luogu P2579 / SSL 2511
题目大意
一个池塘,一些鳄鱼,鳄鱼有周期大于等于二小于等于四的运动路线,你要从某个点恰好走某个步数走到另一个点,然后你不能和任何一个鳄鱼同时存在于一个点上,问你有多少种走法。
方案数对 10000 取模。
思路
鳄鱼的周期看着就很烦,我们就想如果能不管它就好了,直接矩阵乘法。
那我们可以发现鳄鱼的周期只有二、三、四这三种。
那我们就想,我们可以不可以有一个大周期呢?
可以发现,就是
gcd
{
2
,
3
,
4
}
\gcd\{2,3,4\}
gcd{2,3,4},即 12。
那这个我们可以直接枚举每一种情况,然后求出那个时候的矩阵。
某个时候的矩阵,就是在原来的没有鳄鱼的矩阵的基础上改动:任何点到任何鳄鱼的位置所对应的矩阵位置一定是 0。
那一个大周期就是这 12 个矩阵按顺序乘在一起。
然后我们就可以发现,我们可以先乘
k
/
12
k/12
k/12 个大周期矩阵,然后再乘
k
%
12
k\%12
k%12 个那个时候的矩阵。
就是先一个一个周期的处理,没有周期了,就把剩下的一个一个处理掉。
代码
#include<cstdio>
#define mo 10000
using namespace std;
struct matrix {
int n, m;
int a[51][51];
}normal, time[13], all, ANS, E, re;
int n, m, start, end, k, A, B, Nfish, T[21], place[21][5], to, alltime;
matrix operator *(matrix x, matrix y) {
re.n = x.n;
re.m = y.m;
for (int i = 0; i < re.n; i++)
for (int j = 0; j < re.m; j++)
re.a[i][j] = 0;
for (int k = 0; k < x.m; k++)
for (int i = 0; i < re.n; i++)
for (int j = 0; j < re.m; j++)
re.a[i][j] = (re.a[i][j] + (x.a[i][k] * y.a[k][j]) % mo) % mo;
return re;
}
int main() {
scanf("%d %d %d %d %d", &n, &m, &start, &end, &k);
for (int i = 1; i <= m; i++) {
scanf("%d %d", &A, &B);
normal.a[A][B] = normal.a[B][A] = 1;
}
normal.n = n;
normal.m = n;
E.n = n;
E.m = n;
for (int i = 0; i < n; i++)
E.a[i][i] = 1;
all = E;
ANS = E;
scanf("%d", &Nfish);
for (int i = 1; i <= Nfish; i++) {
scanf("%d", &T[i]);
for (int j = 0; j < T[i]; j++) scanf("%d", &place[i][j]);
}
for (int i = 1; i <= 12; i++) {
time[i] = normal;
for (int j = 1; j <= Nfish; j++) {//构造出当前时刻的矩阵
to = place[j][i % T[j]];//哪里有鳄鱼
for (int k = 0; k < n; k++) {//不能走到有鳄鱼的地方
time[i].a[k][to] = 0;
}
}
all = all * time[i];//一个周期的矩阵
}
alltime = k / 12;//算所有周期乘起来
while (alltime) {
if (alltime & 1) ANS = ANS * all;
all = all * all;
alltime >>= 1;
}
alltime = k % 12;//剩下的一些单独算
for (int i = 1; i <= alltime; i++)
ANS = ANS * time[i];
printf("%d", ANS.a[start][end]);
return 0;
}