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;
}
posted @ 2019-10-28 23:54  Hs-black  阅读(142)  评论(0编辑  收藏  举报