zoukankan      html  css  js  c++  java
  • 【ZJOI2005】沼泽鳄鱼 题解报告

    题目描述

    潘塔纳尔沼泽地号称世界上最大的一块湿地,它地位于巴西中部马托格罗索州的南部地区。每当雨季来临,这里碧波荡漾、生机盎然,引来不少游客。

    为了让游玩更有情趣,人们在池塘的中央建设了几座石墩和石桥,每座石桥连接着两座石墩,且每两座石墩之间至多只有一座石桥。这个景点造好之后一直没敢对外开放,原因是池塘里有不少危险的食人鱼。

    豆豆先生酷爱冒险,他一听说这个消息,立马赶到了池塘,想做第一个在桥上旅游的人。虽说豆豆爱冒险,但也不敢拿自己的性命开玩笑,于是他开始了仔细的实地勘察,并得到了一些惊人的结论:食人鱼的行进路线有周期性,这个周期只可能是2,3或者4个单位时间。每个单位时间里,食人鱼可以从一个石墩游到另一个石墩。每到一个石墩,如果上面有人它就会实施攻击,否则继续它的周期运动。如果没有到石墩,它是不会攻击人的。

    借助先进的仪器,豆豆很快就摸清了所有食人鱼的运动规律,他要开始设计自己的行动路线了。每个单位时间里,他只可以沿着石桥从一个石墩走到另一个石墩,而不可以停在某座石墩上不动,因为站着不动还会有其它危险。如果豆豆和某条食人鱼在同一时刻到达了某座石墩,就会遭到食人鱼的袭击,他当然不希望发生这样的事情。

    现在豆豆已经选好了两座石墩Start和End,他想从Start出发,经过K个单位时间后恰好站在石墩End上。假设石墩可以重复经过(包括Start和End),他想请你帮忙算算,这样的路线共有多少种(当然不能遭到食人鱼的攻击)。

    输入输出格式

    输入格式:

     

    输入文件共M + 2 + NFish行。

    第一行包含五个正整数N,M,Start,End和K,分别表示石墩数目、石桥数目、Start石墩和End石墩的编号和一条路线所需的单位时间。石墩用0到N–1的整数编号。

    第2到M + 1行,给出石桥的相关信息。每行两个整数x和y,0 ≤ x, y ≤ N–1,表示这座石桥连接着编号为x和y的两座石墩。

    第M + 2行是一个整数NFish,表示食人鱼的数目。

    第M + 3到M + 2 + NFish行,每行给出一条食人鱼的相关信息。每行的第一个整数是T,T = 2,3或4,表示食人鱼的运动周期。接下来有T个数,表示一个周期内食人鱼的行进路线。

     如果T=2,接下来有2个数P0和P1,食人鱼从P0到P1,从P1到P0,……;

     如果T=3,接下来有3个数P0,P1和P2,食人鱼从P0到P1,从P1到P2,从P2到P0,……;

     如果T=4,接下来有4个数P0,P1,P2和P3,食人鱼从P0到P1,从P1到P2,从P2到P3,从P3到P0,……。

    豆豆出发的时候所有食人鱼都在自己路线上的P0位置,请放心,这个位置不会是Start石墩。

     

    输出格式:

     

    输出路线的种数,因为这个数可能很大,你只要输出该数除以10000的余数就行了。

     

    输入输出样例

    输入样例#1:
    6 8 1 5 3
    0 2
    2 1
    1 0
    0 5
    5 1
    1 4
    4 3
    3 5
    1
    3 0 5 1
    
    输出样例#1:
    2

    说明

     1 ≤ N ≤ 50

     1 ≤ K ≤ 2,000,000,000

     1 ≤ NFish ≤ 20














    每一道debug了很久又很有收获的题都要记下来√



    邻接矩阵乘法

    在写这道题之前,我们先了解邻接矩阵乘法的意义
    什么邻接矩阵还有乘法?
    想想对于,G[i][j],如果存在i->j的边那么G[i][j]=1
    如果我们把这看做G的一次幂,理解为只走一步时i->j的路线条数【当然肯定是1啦。。】

    对于G^2呢?
    对于所有的G[i][k]和G[k][j],都会相乘且结果存在最后的G'[i][j]中,难道这不是走两步时i->j的路线么?

    由此,对于任意G^n[i][j],表示从i出发走n步到j的方案数【可以中途经过j】

    典题:给定一张图,求从i出发走k步到j的方案数。


    题解

    有了这样的知识储备,似乎解出这道题就不是难事了。
    等等。。食人鱼怎么考虑?
    仔细观察,食人鱼的周期很小,为2,3,4,最小公倍数是12
    也就是说,我们可以对12个时间内每个时间点建一个邻接矩阵,然后顺次【注意是顺次,矩阵乘法没有交换律】相乘
    为了降低复杂度,其中[K/12]组可以放在一起用快速幂算出

    坑点:
    1、注意正确把握周期时间点,从A[1]乘到A[11]再乘A[0]
    1、矩阵乘法没有交换律,得先预处理Q=A[1]*A[2]*......*A[11]*A[0],再用算出Q^[K/12],对于剩下的,从A[1]开始乘完K%12个【这个坑了我好久】
    3、标号从0开始= =

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    using namespace std;
    const int maxn=55,INF=2000000000,P=10000;
    
    inline int read(){
    	int out=0,flag=1;char c=getchar();
    	while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
    	while(c>=48&&c<=57){out=out*10+c-48;c=getchar();}
    	return out*flag;
    }
    
    struct Matrix{
    	LL s[maxn][maxn],n,m;
    	Matrix() {n=m=0;memset(s,0,sizeof(s));}
    }A[20],Q;
    
    inline Matrix operator *(const Matrix& a,const Matrix& b){
    	Matrix c;
    	if(a.m!=b.n) return c;
    	c.n=a.n;c.m=b.m;
    	for(int i=1;i<=c.n;i++)
    		for(int j=1;j<=c.m;j++)
    			for(int k=1;k<=a.n;k++)
    				c.s[i][j]=(c.s[i][j]+a.s[i][k]*b.s[k][j])%P;
    	return c;
    }
    
    Matrix qpow(Matrix a,LL b){
    	Matrix ans;
    	ans.n=ans.m=a.n;
    	for(int i=1;i<=ans.n;i++) ans.s[i][i]=1;
    	for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
    	return ans;
    }
    
    int N,M,S,E,K,uns[20][maxn],G[maxn][maxn];
    
    void init(){
    	N=read();M=read();S=read()+1;E=read()+1;K=read();
    	int a,b;
    	while(M--){
    		a=read()+1;
    		b=read()+1;
    		G[a][b]=G[b][a]=1;
    	}
    	int NF=read(),T,u;
    	while(NF--){
    		T=read();
    		for(int i=0;i<T;i++){
    			u=read()+1;
    			for(int j=i;j<12;j+=T)
    				uns[j][u]=true;
    		}
    	}
    	Q.n=Q.m=N;
    	for(int i=1;i<=N;i++) Q.s[i][i]=1;
    	for(int t=0;t<12;t++){
    		A[t].n=A[t].m=N;
    		for(int i=1;i<=N;i++)
    			for(int j=1;j<=N;j++){
    				A[t].s[i][j]=(G[i][j]&&!uns[t][j]);
    			}
    	}
    	for(int i=1;i<12;i++) Q=Q*A[i];
    	Q=Q*A[0];
    }
    
    void solve(){
    	int k=K/12,t=K-k*12;
    	Matrix ans;ans.n=ans.m=N;
    	for(int i=1;i<=N;i++) ans.s[i][i]=1;
    	ans=ans*qpow(Q,k);
    	for(int i=1;i<=t;i++) ans=ans*A[i];
    	cout<<ans.s[S][E]<<endl;
    }
    
    int main(){
    	init();
    	/*for(int i=1;i<=N;i++){
    		for(int j=1;j<=N;j++) cout<<G[i][j]<<' ';
    		cout<<endl;
    	}
    	for(int t=0;t<12;t++){
    		cout<<endl<<endl;
    		for(int i=1;i<=N;i++){
    			for(int j=1;j<=N;j++) cout<<A[t].s[i][j]<<' ';
    			cout<<endl;
    		}
    	}*/
    	solve();
    	return 0;
    }
    



  • 相关阅读:
    【转】【SEE】基于SSE指令集的程序设计简介
    【转】【Asp.Net】asp.net服务器控件创建
    ControlTemplate in WPF ——ScrollBar
    ControlTemplate in WPF —— Menu
    ControlTemplate in WPF —— Expander
    ControlTemplate in WPF —— TreeView
    ControlTemplate in WPF —— ListBox
    ControlTemplate in WPF —— ComboBox
    ControlTemplate in WPF —— TextBox
    ControlTemplate in WPF —— RadioButton
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282884.html
Copyright © 2011-2022 走看看