题目链接:P4159 [SCOI2009] 迷路
题意:该有向图有 n n n 个节点,节点从 1 1 1 至 n n n 编号,windy 从节点 1 1 1 出发,他必须恰好在 t t t 时刻到达节点 n n n。
现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?
答案对 2009 2009 2009 取模。
注意:windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
题目给出了邻接矩阵
首先,根据矩阵乘法的性质,我们可以发现
不考虑题目的限制,当矩阵仅由01构成时
M 2 M^2 M2 的意义就是只通过两条边(此时边权均为 1 1 1 )能到达每个结点的方案数
则此时答案就是 M t M^t Mt
可是题目给出的矩阵,至多有 10 10 10
怎么办呢?考虑把它转化新的矩阵(仅有01构成的)
对于初始矩阵的每个节点,把它化为 x i x_i xi 个结点
其中 x i x_i xi 表示出边边权的最大值
如下图
“拆点”操作
可以发现这样的话仍然是满足题意的
为什么呢?因为可以更新答案的那条路径
一定走到了 x i x_i xi 号节点,正准备走到原来的另一个节点
不是很好解释,自己多画几张图试试就知道了(我知道你们懒,给你们画一个)
“拆点”操作
那么这题就很简单了
时间复杂度 O ( n 3 log t ) O(n^3log t) O(n3logt)
代码如下
#includeusing namespace std; #define int long long #define INF ((int)0x3f3f3f3f3f3f3f3f) #define inf ((int)0xc0c0c0c0c0c0c0c0) #define N (int)(25) const int p=2009; int n,t; struct mat { int n,g[205][205]; void clear() { memset(g,0,sizeof(g)); } mat operator*(const mat &o)const { static mat tmp; tmp.n=n;tmp.clear(); for(int i=1; i<=n; i++) for(int k=1; k<=n; k++) { int r=g[i][k]%p; for(int j=1; j<=n; j++) tmp.g[i][j]=(tmp.g[i][j]+r*o.g[k][j]%p)%p; } return tmp; } }M; mat qpow(mat a,int b) { static mat ans,base=a; ans.n=a.n;ans.clear(); for(int i=1; i<=ans.n; i++) ans.g[i][i]=1; while(b) { if(b&1)ans=ans*base; base=base*base; b>>=1; } return ans; } int ck(int i,int j) { return (i-1)*10+j; } signed main() { scanf("%lld%lld",&n,&t); M.n=n*10;M.clear(); for(int i=1; i<=n; i++) { for(int j=1; j<10; j++) M.g[ck(i,j)][ck(i,j+1)]=1; for(int j=1,x; j<=n; j++) { scanf("%1lld",&x); if(x)M.g[ck(i,x)][ck(j,1)]=1; } } // for(int i=1; i<=M.n; i++) // for(int j=1; j<=M.n; j++) // printf("%lld%c",M.g[i][j]," n"[j==M.n]); M=qpow(M,t); printf("%lldn",M.g[1][ck(n,1)]); return 0; }
转载请说明出处


![洛谷P4159 [SCOI2009] 迷路 题解 洛谷P4159 [SCOI2009] 迷路 题解](http://www.mshxw.com/aiimages/31/835743.png)
