BZOJ1898. [Zjoi2005]Swamp 沼泽鳄鱼
机房大佬讲解后, 写一写题解
这是很经典的邻接矩阵乘法了
对于邻接矩阵G[i][j] 表示i到j有没有边
如果是\(G^1\) , 发现就是原矩阵, G[i][j]可以理解为从i走一步到j的方案数
下面考虑\(G^2\)
矩阵乘法代码:
Matrix operator * (const Martix &p) const{
Martix tmp;
for (rint i = 0;i < n; i++)
for (rint j = 0;j < n; j++)
for (rint k = 0;k < n; k++)
tmp.Mar[i][j] = (tmp.Mar[i][j] + Mar[i][k] * p.Mar[k][j]) % P;
return tmp;
}
发现\(G^2[i][j] = \sum_{k=0}^{n-1} G[i][k] * G[k][j]\) 像不像Floyd
即枚举一个中间点k, 从i到j走两步的的方案数等于从i到走一步k的方案数, 再乘上从k走一步到j的方案数之和
以此类推
\(G^t[i][j]\)为再G这个邻接矩阵上从i走t步到j的方案数
原矩阵\(G^0\)为单位矩阵, 每乘一个新的邻接矩阵相当于又在新的邻接矩阵上走一步
回到本题
2, 3, 4的最小公倍数为12
所以开十二个邻接矩阵即可, 每个矩阵表示当前走一步的合法情况
a[0]表示走12步的矩阵, 即12个邻接矩阵之积
k/12个a[0]相乘使用快速幂, 在按顺序从a[1]乘到a[k%12]
答案为 \(a[0]^{k/12} * a[1] * a[2] * \cdots s[k\%12]\)
细节见代码及注释:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
template <typename T>
void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x = (x << 3) + (x << 1) + c - '0';
x *= f;
}//快读
const int N = 52;
int n, m;
ll t, k;
const int P = 10000;
#define rint register int
struct matrix{
int Mar[N][N];
matrix () {
memset(Mar, 0, sizeof(Mar));
}
matrix operator * (const matrix &p) const{
matrix tmp;
for (rint i = 0;i < n; i++)
for (rint j = 0;j < n; j++)
for (rint k = 0;k < n; k++)
tmp.Mar[i][j] = (tmp.Mar[i][j] + Mar[i][k] * p.Mar[k][j]) % P;
return tmp;
}//矩阵乘法
void print(void) {
for (int i = 0;i < n; i++) {
for (int j = 0;j < n; j++)
printf ("%d ", Mar[i][j]);
cout << endl;
}
cout << endl;
}//调试输出
}a[15], b, c;
matrix pow() {
matrix ans = b, tmp = a[0];
int tttmp = k / 12;
while (tttmp) {
if (tttmp & 1) ans = ans * tmp;
tmp = tmp * tmp;
tttmp >>= 1;
}
return ans;
}//矩阵快速幂
int nfish, ttmp[50], st, ed;
int main() {
// freopen("hs.out","w",stdout);
read(n), read(m), read(st), read(ed), read(k);
while (m--) {
int x, y;
read(x), read(y);
c.Mar[x][y] = c.Mar[y][x] = 1; //原矩阵
}
// c.print();
for (rint i = 1;i <= 12; i++) a[i] = c; //初始化
for (rint i = 0;i < n; i++) b.Mar[i][i] = 1; //单位矩阵
read(nfish);
while (nfish--) {
read(t);
for (int i = 0;i < t; i++) read(ttmp[i]); ttmp[t] = ttmp[0];
//由于开始时时间为0, 所以读入要从零开始
for (int i = 1;i <= 12; i++)
for (int j = 0;j < n; j++)
a[i].Mar[j][ttmp[(i-1)%t+1]] = 0;
//注意只清空从其他地方到鳄鱼新来的地点的边
}
// for (int i = 1;i <= 12; i++) a[i].print();
a[0] = b;
for (int i = 1;i <= 12; i++) a[0] = a[0] * a[i];
// a[0].print();
matrix ans = pow();
for (int i = 1;i <= k % 12; i++) ans = ans * a[i];
cout << ans.Mar[st][ed] << endl;
return 0;
}