Loading

题解-[ZJOI2005]沼泽鳄鱼

题解-[ZJOI2005]沼泽鳄鱼

前置知识:

邻接矩阵

矩阵乘法

矩阵快速幂


[ZJOI2005]沼泽鳄鱼

给一个有 \(N\) 个点,\(M\) 条双向边的图 \(G\),其中有 \(NFish\) 只鳄鱼以 \(T\) 个点 \(P_0\sim P_t\) 为周期运动。求从 \(Start\) 出发到 \(End\) 不停留走 \(K\) 步每步不碰到鳄鱼的方案数(节点下标从 \(0\) 开始编号)。

数据范围:\(1\le N\le 50\)\(1\le K\le 2\times 10^9\)\(1\le NFish\le 20\)\(2\le T\le 4\)


看到这个 \(K\) 的范围就知道要带个 \(\log\),而在图上可以带 \(\log\) 的算法,唯有二分、倍增(树形图)和邻接矩阵快速幂(小图)。看到这题 \(N\) 很小,又无从二分,便知道可以邻接矩阵快速幂


首先要知道什么是矩阵乘法。大小为 \(a\times b\) 的矩阵 \(A\) 和大小为 \(b\times c\) 的矩阵方可相乘,乘积矩阵 \(C\) 大小为 \(a\times c\),满足 \(C_{i,j}=\sum\limits_{k=1}^bA_{i,k}\cdot B_{k,j}(1\le i\le a,1\le j\le c)\)

矩阵乘法可以解决集合的定向求和变换操作。其中原矩阵乘以变换矩阵变成目标矩阵。


矩阵快速幂:原矩阵多次定向变换,可以通过多次乘以变换矩阵解决。矩阵乘法满足结合律,所以可以先求出变换矩阵的幂。矩阵也可以快速幂,时间复杂度 \(\Theta(n^3\log k)\)


邻接矩阵:用于表示图边,在无权图中,如果矩阵 \(M\) 的元素 \(M_{i,j}=1\),表示存在边 \((i,j)\)


而邻接矩阵快速幂就是建立在邻接矩阵和矩阵快速幂上的。原矩阵表示起点状态,变换矩阵为邻接矩阵,每乘一次就表示不停留地走一步后的状态,乘 \(K\) 次就成了最终状态,取终点矩阵值则为答案。

状态:到每个节点的方案数。


Example

讲解时暂时初始下标为 \(1\) 吧,暂时不考虑鳄鱼。

例如 \(N=3\)\(Start=1\)\(End=3\),有双向边 \({(1,3),(3,2)}\),求走 \(K=3\) 步后到终点 \(End\) 的方案数。

原始状态:

\[A= \begin{bmatrix} 1 & 0 & 0\\ \end{bmatrix} \]

变换矩阵为邻接矩阵:

\[B= \begin{bmatrix} 0 & 0 & 1\\ 0 & 0 & 1\\ 1 & 1 & 0\\ \end{bmatrix} \]

目标状态:

\[\begin{split} C=&A\times B^K\\ =& \begin{bmatrix} 1 & 0 & 0\\ \end{bmatrix} \times \begin{bmatrix} 0 & 0 & 1\\ 0 & 0 & 1\\ 1 & 1 & 0\\ \end{bmatrix} ^3\\ =& \begin{bmatrix} 1 & 0 & 0\\ \end{bmatrix} \times \begin{bmatrix} 0 & 0 & 2\\ 0 & 0 & 2\\ 2 & 2 & 0\\ \end{bmatrix}\\ =& \begin{bmatrix} 0 & 0 & 2\\ \end{bmatrix} \end{split} \]

所以到终点 \(End=3\) 的方案数为 \(2\)


但是鳄鱼怎么办呢?

可以发现 \(2\le T\le 4\),所以所有鳄鱼运动的总周期是 \(12\)。可以计算 \(12\) 种变换邻接矩阵,表示到每个周期时可以走的边。然后顺次相乘,求乘积的 \(\lfloor\frac K{12}\rfloor\) 次幂(可以用矩阵快速幂)乘以 前 \(\left(K\bmod 12\right)\) 种变换矩阵的乘积,就是总变换矩阵。用表示起点的原矩阵乘以总变换矩阵,即可得答案。

时间复杂度 \(\Theta(NFish+N^3\log K)\)(注意,\(NFish\) 为一个完整变量名)。


真的难讲,还是放代码吧。

Code

#include <bits/stdc++.h>
using namespace std;

//&Start
#define inf 0x3f3f3f3f
#define re register
#define il inline
#define hash unorded_map
typedef long long lng;
typedef unsigned long long ulng;
typedef vector<int> veci;
#define fo(i,st,xb,y) for(re int i=st;i xb;i y)

//&Data
#define N 50
#define mod 10000
int n,m,s,t,k,fish,p[25][12];

//&Matrix
struct Matrix{ //矩阵
	int arr[N+5][N+5];
	Matrix(re int op=0){
		memset(arr,0,sizeof arr);
		if(op==1) fo(i,1,<=N,++) arr[i][i]=1; //构造单位矩阵 E 满足 E*A=A*E=A
	}
	il int* operator[](re int x){return arr[x];} 
	il friend Matrix operator*(re Matrix x,re Matrix y){
		re Matrix res;
		fo(k,1,<=N,++)fo(i,1,<=N,++)fo(j,1,<=N,++)
			(res[i][j]+=x[i][k]*y[k][j]%mod)%=mod;
		return res;
	}
	il void print(re char*s){
		puts(s);
		fo(i,1,<=n,++)fo(j,1,<=n,++)
			printf("%d%c",arr[i][j],"\n "[j<n]);	
	}
}st,e,g[12],all,ans;
il Matrix Pow(re Matrix a,re int x){ //矩阵快速幂
	re Matrix res(1);
	for(;x;a=a*a,x>>=1)if(x&1) res=res*a;
	return res;
}

//&Main
int main(){
	scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
	s++,t++,st[1][s]=1; //原矩阵
	fo(i,1,<=m,++){
		re int u,v;scanf("%d%d",&u,&v);
		e[u+1][v+1]=e[v+1][u+1]=1; //邻接矩阵
	}
	scanf("%d",&fish);
	fo(i,1,<=fish,++){
		re int tmp; scanf("%d",&tmp);
		fo(j,0,<tmp,++) scanf("%d",p[i]+j),p[i][j]++;
		fo(j,tmp,<12,++) p[i][j]=p[i][j%tmp];
	}
	re int fb[N+5];
	fo(i,0,<12,++){
		fill(fb+1,fb+n+1,0);
		fo(j,1,<=fish,++) fb[p[j][i]]=1;
		fo(j,1,<=n,++)fo(k,1,<=n,++)
			if(!fb[k]) g[i][j][k]=e[j][k]; //12种变换矩阵
	}
	all=Matrix(1);
	fo(i,1,<12,++) all=all*g[i];
	all=all*g[0]; //12个矩阵顺次相乘(1,2,...,11,0)
	ans=st*Pow(all,k/12); //原矩阵乘以变换矩阵
	fo(i,1,<=k%12,++) ans=ans*g[i]; //乘以剩余 K%12 个矩阵
	printf("%d\n",ans[1][t]); //答案
	return 0;
}

祝大家学习愉快!

posted @ 2020-03-22 13:42  George1123  阅读(159)  评论(0编辑  收藏  举报