[TJOI2017][BZOJ4887]可乐 解题报告
传送门:戳我qwq
首先先介绍一个性质,一张图的邻接矩阵的n次方后得到,矩阵A,A(i,j)表示在图中从 i 节点出发走了 n 步,到达 j 节点的可能方案数。这里不加证明,读者可以联系矩阵乘法的性质模拟一下。
我们考虑到机器人有爆炸的可能性,且爆炸后不能继续走动,且它可以待在原地不动。
那么不难想到,待在原地不动的情况可以用自环来解决。
机器人在任何一步都可能爆炸,机器人爆炸后,该状态将不对其他任何状态产生影响。
应该有读者已经想到了,我们可以把所有点都连向一个新建的节点,且该节点没有出度,机器人爆炸实际上就相当于被发配边疆且永远不能回来(雾。
既然已经考虑清楚,那么我们只需要对原始领接矩阵S进行改动,所有S[i][0]=1 ,S[i][i]=1 。
然后按照文章开头介绍的性质,A=S^t。A就是我们的答案矩阵。
于是我的代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; inline void read(int &x) { x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } int N; struct node{ int max[35][35]; friend node operator * (node a,node b) { node c; for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) c.max[i][j]=0; for(int i=0;i<=N;i++) { for(int j=0;j<=N;j++) { for(int k=0;k<=N;k++) { c.max[i][j]+=a.max[i][k]*b.max[k][j]%2017; c.max[i][j]%=2017; } } } return c; } }; int M,t; node qpow(node st,int t) { node base=st,ans; for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) ans.max[i][j]=0; for(int i=0;i<=N;i++) ans.max[i][i]=1; while(t) { if(t&1) ans=ans*base; base=base*base; t>>=1; } return ans; } int main() { read(N);read(M); node st; for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) st.max[i][j]=0; int u,v; for(int i=1;i<=M;i++) { read(u);read(v); st.max[u][v]=1; st.max[v][u]=1; } read(t); for(int i=1;i<=N;i++) st.max[i][0]=1; for(int i=0;i<=N;i++) st.max[i][i]=1; node ans=qpow(st,t); int tot=0; for(int i=0;i<=N;i++) { tot+=ans.max[1][i]; tot%=2017; } printf("%d\n",tot); }