Bzoj P1898 [ZJOI2005]沼泽鳄鱼___矩阵乘法

题目大意:

一张无向图,有 n n n个点, m m m条无向边
规定起点为 S S S,终点为 T T T
每个一个单位时间只能移动一次
一些食人鱼作周期运动,人在任意单位时间不能碰到食人鱼
要求经过 K K K个单位时间后恰好到达 T T T
问合法路径方案总数,这个结果对 1 e 4 1e4 1e4取模

1 ≤ 食 人 鱼 个 数 ≤ 20 1 ≤ 食人鱼个数 ≤ 20 120
2 ≤ 周 期 运 动 长 度 ≤ 4 2≤周期运动长度≤4 24
K < = 2 ∗ 1 0 9 K<=2∗10^9 K<=2109

分析:

考虑没有食人鱼的情况,
f k , i , j f_{k,i,j} fk,i,j表示走了 k k k步,位置 i i i j j j的路径方案总数
显然一开始 f 1 , i , j = 1 ( i , j 连 通 ) f_{1,i,j}=1(i,j连通) f1,i,j=1(i,j)否则 f 1 , i , j = 0 f_{1,i,j}=0 f1,i,j=0
然后每次转移即 f k , i , j = Σ l = 1 n f k − 1 , i , l ∗ f 1 , l , j f_{k,i,j}=Σ_{l=1}^{n}f_{k-1,i,l}*f_{1,l,j} fk,i,j=Σl=1nfk1,i,lf1,l,j
这一看不就是矩阵乘法吗,
显然最后的答案得到的矩阵为 f 1 K {f_1}^K f1K
设最后得到的矩阵为 C C C,则 C S , T C_{S,T} CS,T即为所求
此时对于存在食人鱼的情况,
观察到周期长度只有 2 , 3 , 4 2,3,4 2,3,4 l c m ( 2 , 3 , 4 ) = 12 lcm(2,3,4)=12 lcm(2,3,4)=12
那么我们就可以以 12 12 12为一个周期去搞定每个单位时间图的合法连通情况,
即设 U s e T i m e k UseTime_k UseTimek表示单位时间为 L ∗ 12 + k L*12+k L12+k时图的合法连通情况 ( L ∈ Z ) (L∈Z) (LZ)
我们发现如果一个食人鱼在单位时间 L ∗ 12 + k + 1 L*12+k+1 L12+k+1会出现在位置 x x x,那么这个单位时间 L ∗ 12 + k L*12+k L12+k所有连向 x x x的边都应该断掉,因为必定不合法,然后综合一下原图的连通情况即可求得 U s e T i m e k UseTime_k UseTimek
那么答案显然就是各个单位时间的图的连通情况的乘积
为了优化时间,我们可以设 S u m = ∏ i = 1 12 U s e T i m e i Sum=\prod_{i=1}^{12} UseTime_i Sum=i=112UseTimei
然后最后的答案矩阵 A n s w e r Answer Answer即为
S u m ⌊ K / 12 ⌋ ∗ ∏ i = 1 K m o d 12 U s e T i m e i Sum^{\left \lfloor K/12 \right \rfloor}*\prod_{i=1}^{Kmod12}UseTime_i SumK/12i=1Kmod12UseTimei
前面矩阵快速幂,后面暴力算即可,注意矩阵乘法不满足交换律,要顺次算
最后合法方案总数就是 A n s w e r S , T Answer_{S,T} AnswerS,T

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define modn 10000
#define N 55

using namespace std;

typedef long long ll;

struct Matrix { int Num[N][N]; };

Matrix UseTime[15], AwekTime, Total, C, A;
int KDA[N], n, m, S, T, K, NFish;

void mul(Matrix AA, Matrix BB, int opt)
{
	memset(C.Num, 0, sizeof(C.Num));
	for (int i = 1; i <= n; i++)
	    for (int j = 1; j <= n; j++)
			for (int k = 1; k <= n; k++) 
	            C.Num[i][j] = (C.Num[i][j] + AA.Num[i][k] * BB.Num[k][j]) % modn;
	if (opt == 1) memcpy(AwekTime.Num, C.Num, sizeof(C.Num));
	if (opt == 2) memcpy(Total.Num, C.Num, sizeof(C.Num));
}

void ksm(int x)
{	
	for (int i = 1; i <= n; i++) AwekTime.Num[i][i] = 1;
	for (int i = 1; i <= 12; i++) mul(AwekTime, UseTime[i], 1);
	for (; x; x >>= 1)
	{
	    if (x & 1) mul(Total, AwekTime, 2);
		mul(AwekTime, AwekTime, 1);	
	}
}

int main()
{
	scanf("%d %d %d %d %d", &n, &m, &S, &T, &K);
	for (int i = 1; i <= m; i++)
	{
		int x, y; scanf("%d %d", &x, &y); 
		A.Num[x + 1][y + 1] = A.Num[y + 1][x + 1] = 1;
	}
	for (int i = 1; i <= 12; i++)
	    memcpy(UseTime[i].Num, A.Num, sizeof(A.Num));
	scanf("%d", &NFish);
	while (NFish--)
	{
		int yzh; scanf("%d", &yzh); 
		for (int i = 1; i <= yzh; i++) scanf("%d", &KDA[i]);
		int ID = 1;
		for (int i = 1; i <= 12; i++)
		{
			++ID; if (ID == yzh + 1) ID = 1;			
			for (int j = 1; j <= n; j++) UseTime[i].Num[j][KDA[ID] + 1] = 0;
		}
	}
	for (int i = 1; i <= n; i++) Total.Num[i][i] = 1;	
	ksm(K / 12);
	for (int i = 1; i <= K % 12; i++) mul(Total, UseTime[i], 2);
	printf("%d\n", Total.Num[S + 1][T + 1]);
	return 0;
}

你可能感兴趣的:(C++,快速幂,矩阵乘法)