zoukankan      html  css  js  c++  java
  • 题解-[ZJOI2005]沼泽鳄鱼

    题解-[ZJOI2005]沼泽鳄鱼

    前置知识:

    邻接矩阵

    矩阵乘法

    矩阵快速幂


    [ZJOI2005]沼泽鳄鱼

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

    数据范围:(1le Nle 50)(1le Kle 2 imes 10^9)(1le NFishle 20)(2le Tle 4)


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


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

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


    矩阵快速幂:原矩阵多次定向变换,可以通过多次乘以变换矩阵解决。矩阵乘法满足结合律,所以可以先求出变换矩阵的幂。矩阵也可以快速幂,时间复杂度 (Theta(n^3log 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= egin{bmatrix} 1 & 0 & 0\ end{bmatrix} ]

    变换矩阵为邻接矩阵:

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

    目标状态:

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

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


    但是鳄鱼怎么办呢?

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

    时间复杂度 (Theta(NFish+N^3log 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],"
     "[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
    ",ans[1][t]); //答案
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    图片加载库Glide
    Home键和back键下 Activity的生命周期变化
    Fragment重叠问题
    Fragment与Activiy之间的交互
    android事件拦截处理机制详解 .--------转
    实现手机QQ的抖动效果
    点评点赞功能的基本实现------个人观点
    自定义侧滑菜单
    检查设备剩余内存
    StringByAppendingPathComponent和stringByAppendingString的区别
  • 原文地址:https://www.cnblogs.com/George1123/p/12545665.html
Copyright © 2011-2022 走看看