P3758 TJOI2017 可乐
记录边权的邻接矩阵
\(D_{i,j}=+\infty(i=j)||i与j间边权,i与j联通,+\infty(i,j不连通)\)
\(D_{(i,j)}^2=min_{i\le k\le n}D_{i,k}+D_{k,j}\)
\(D^K_{i,j}表示i到j经过k条边的最短路\)
记录连通性的矩阵意义等价
\(A^k_{i,j}表示由i到j经过k条边的方案数\)
P3758 TJOI2017 可乐
总方案数=\(t秒后处于各个位置的可能+所有已经爆炸的方案\)
在原先 n 个点的基础上加一个点 0,表示机器人自爆后的去处,每个点都向 0 连一条单向边
对于每个点可以添加一个自环,走这个自环就代表停在这里,那么每秒就都可以走一步
只需要建出邻接矩阵,然后矩阵快速幂求出点 1 到每一点的方案数,求和即可
#include<cstdio>
#include<cstring>
#define mod 2017
#define int long long
using namespace std;
int answ = 0,n,m;
struct mat
{
int a[101][101];
mat(){memset(a,0,sizeof(a));}
void qwq(){for(int i=1;i<=n;i++)a[i][i]=1;}
}a,ans;
mat operator *(const mat &x,const mat &y)
{
mat z;
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
return z;
}
signed main(){
scanf("%lld%lld",&n,&m);
n++;
int x,y,t;
for(int i = 1;i <= m;i++){
scanf("%lld%lld",&x,&y);
a.a[x][y] = 1; a.a[y][x] = 1;
}
for(int i=1;i<=n;i++)a.a[i][n]=1,a.a[i][i]=1;
ans.qwq();
scanf("%lld",&t);
while(t){
if(t & 1) ans = ans * a;
t >>=1;
a = a * a;
}
for(int i = 1;i <= n;i++)
answ = (ans.a[1][i] + answ) % mod;
printf("%lld",answ);
}