洛谷P3758/BZOJ4887 [TJOI2017] 可乐 [矩阵快速幂]

时间:2023-03-09 17:08:00
洛谷P3758/BZOJ4887 [TJOI2017] 可乐 [矩阵快速幂]

  洛谷传送门BZOJ传送门

可乐

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 299  Solved: 207

Description

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且
放在了加里敦星球的1号城市上。这个可乐机器人有三种行为:停在原地,去下一个相邻的
城市,自爆。它每一秒都会随机触发一种行为。现在给出加里敦星球城市图,在第0秒时可
乐机器人在1号城市,问经过了t秒,可乐机器人的行为方案数是多少?

Input

第一行输入两个正整数N,M表示城市个数,M表示道路个数。(1≤N≤30,0≤M≤100)
接下来M行输入u,v表示u,v之间有一条道路。
(1≤u,v≤n)保证两座城市之间只有一条路相连。
最后输入时间t。1<t≤10^6

Output

输出可乐机器人的行为方案数,答案可能很大,请输出对2017取模后的结果。

Sample Input

3 2
1 2
2 3
2

Sample Output

8

HINT

Source


  分析:

  一道人类智慧题,思路无比妙。

  大多数人第一眼看到这题反应应该都是$DP$,出题人貌似也没想卡一般的$DP$(用$DP$+矩阵加速也是可以以非常优秀的效率过掉的),因此写的好看的$DP$也可以过,不过可能会需要吸一口氧气。

  正解是矩阵快速幂。

  嗯???矩阵快速幂??这题和矩阵快速幂有关系??是的,正解就是矩阵快速幂。juruo一开始也没想到,还是看了一位julao的思路才豁然开朗。

  首先看,$n$的范围只有$30$,明显可以用邻接矩阵。而这题的突破口就在这里。我们来思考,如果对邻接矩阵$A$做快速幂会怎样?

  从$Floyd$算法的角度分析,没有边权时(即默认所有边的边权为$1$时),$A^k$中的任意一个元$a_{i,j}$表示从$i$到$j$经过$k$条边的方案数。这个不太方便字面上解释,可以自己根据矩阵乘法的法则结合具体例子分析一下。

  对于这题,因为可以留在原地,所以我们可以把所有点都加上一个自环。还有爆炸的情况,我们可以把爆炸当作第$0$号点,并单方面建立所有点到$0$号点的单向边,这样的过的话到达$0$号点以后就不会再到其他点,就能表示出爆炸的情况了。最后统计的答案应该就是从$1$号点到所有点经过$k$条边的情况总和,也就是$\sum ^n_{i=0}a[1][i]$。

  剩下的就是矩阵快速幂的模板了。

  不得不说,真是一道人类智慧题。

  Code:

  

//It is made by HolseLee on 6th Sep 2018
//Lougu.org P3758
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int mod=;
int n,m,t,ans;
struct Matrix {
int a[][];
Matrix() { memset(a,,sizeof(a)); }
Matrix(int b[][]) { memcpy(a,b,sizeof(a)); }
friend Matrix operator * (const Matrix x,const Matrix y) {
Matrix ret;
for(int i=; i<=; ++i)
for(int j=; j<=; ++j)
for(int k=; k<=; ++k) {
ret.a[i][j]=(ret.a[i][j]+(x.a[i][k]*y.a[k][j]))%mod;
}
return ret;
}
}H,L; int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=; i<=m; ++i) {
scanf("%d%d",&x,&y);
H.a[x][y]=H.a[y][x]=;
}
for(int i=; i<=n; ++i) H.a[i][]=, H.a[i][i]=;
for(int i=; i<=n; ++i) L.a[i][i]=;
scanf("%d",&t);
while( t ) {
if( t& ) L=L*H;
t>>=; H=H*H;
}
for(int i=; i<=n; ++i)
ans=(ans+L.a[][i])%mod;
printf("%d",ans);
return ;
}