沼泽鳄鱼

题目link:https://www.luogu.com.cn/problem/P2579


Part1:

首先不看鳄鱼,那么题目要求的就是豆豆从 $s$ 走到 $e$ 并走过 $k$ 条边的方案数(这里每过一个石桥需要 $k$ 单位的时间,因此也可将其看作走过 $k$ 条边)。

这里不难想到图论中邻接矩阵记作 $E(n$$,$ $n)$ 的一个特性,便是 $E^a$ 得到的矩阵 $E'$中,$E'$$i,j$ 表示从 $i$ 到 $j$ 经过 $a$ 条边的路径方案数,根据矩阵乘法的定义及公式不难证明。

那么现在没有鳄鱼的情况就可以求出来了,根据邻接矩阵的特性容易得出答案为 $E^k$$s,e$,而这个 $k$ 次方可以用矩阵快速幂来求出。


Part2:

既然没有鳄鱼的情况能够求出来了,那么来看看加上鳄鱼后这个邻接矩阵有什么变化。

容易发现在某一时刻,如果有一个鳄鱼在第 $u$ 个石墩上,那么邻接矩阵的第 $u$ 列都会变成 $0$ 。

这样以来,便容易求出某一时刻的邻接矩阵(方法即为当前时间对鳄鱼的周期取余并修改邻接矩阵)。

再来看看邻接矩阵的特性在其变化后是否仍然成立(这里这个特性改为在用当前时间的邻接矩阵 $*$ 后一单位时间的邻接矩阵 $*$ $...$ $*$ 后 $a$ 单位时间的邻接矩阵后得到的矩阵 $E'(n,$ $n)$ 中,$E'$$i,j$ 表示从 $i$ 到 $j$ 经过 $a$ $+$ $1$ 条边的路径方案数)。当然容易得出是成立的,继续利用矩阵乘法的定义及公式不难证明。

现在,似乎已经可以求出在有鳄鱼的情况下的答案了(用每单位时间的邻接矩阵相乘),但是,题目中 $k$ 的大小为 $2$ $*$ $10^9$,直接炸掉。


Part3:

考虑优化,发现题目中还有一个条件——鳄鱼的周期大小只可能为 $2,3,4$。

这提供一个什么信息呢?那便是每经过 $12$ 个周期($2,3,4$ 的最小公倍数),所有鳄鱼的位置便会循环一次,而这则是解决问题的关键。

因为得到了上面的结论,容易得出如果将每单位时间的邻接矩阵都列出,那么相隔 $12$ 个矩阵的邻接矩阵便是一样的,因此只会有 $12$ 个不同的邻接矩阵,且它们都是顺序排列的。

这样问题就迎刃而解了,先将前 $12$ 个单位时间的邻接矩阵求出,求出它们乘积的 $(k$ $/$ $12)$ 次方(矩阵快速幂解决),并对 $k$ $/$ $12$ 的余数进行一些处理。


时间复杂度 $Ο(n^3$ $*$ $log$ $k)$ 。


Code:

  1 #include <cstdio>
  2 
  3 const int MAXN = 50;
  4 const int MAXNF = 20;
  5 const int MOD = 10000;
  6 
  7 int n, m, s, e, k, nf, T[MAXNF + 5], p[MAXNF + 5][5];
  8 
  9 struct Mat {
 10 
 11     int N, arr[MAXN + 5][MAXN + 5];
 12 
 13     Mat() {}
 14     Mat(int _N, int fgr = 0) {
 15         N = _N;
 16         for(int i = 0; i < N; ++i) {
 17             for(int j = 0; j < N; ++j) {
 18                 arr[i][j] = (i == j) ? fgr : 0;
 19             }
 20         }
 21     }
 22 
 23     void Prt() {
 24         for(int i = 0; i < N; ++i) {
 25             for(int j = 0; j < N; ++j) {
 26                 printf("%d%c", arr[i][j], (j == N - 1) ? '
' : ' ');
 27             }
 28         }
 29         puts("");
 30     }
 31 
 32     void Dlt(int pos) {
 33         for(int i = 0; i < N; ++i) {
 34             arr[i][pos] = 0;
 35         }
 36     }
 37 
 38 };
 39 
 40 Mat operator* (const Mat &A, const Mat &B) {
 41 
 42     Mat res(A.N);
 43 
 44     for(int i = 0; i < res.N; ++i) {
 45         for(int j = 0; j < res.N; ++j) {
 46             for(int k = 0; k < res.N; ++k) {
 47                 res.arr[i][j] += A.arr[i][k] * B.arr[k][j];
 48                 res.arr[i][j] %= MOD;
 49             }
 50         }
 51     }
 52 
 53     return res;
 54 }
 55 
 56 Mat qpw(Mat A, int fgr) {
 57 
 58     Mat res(n, 1);
 59 
 60     for(; fgr; fgr >>= 1) {
 61         if(fgr & 1) res = res * A;
 62         A = A * A;
 63     }
 64 
 65     return res;
 66 }
 67 
 68 int main() {
 69 
 70     scanf("%d %d %d %d %d", &n, &m, &s, &e, &k);
 71 
 72     Mat adj(n);
 73     for(int i = 1, x, y; i <= m; ++i) {
 74         scanf("%d %d", &x, &y);
 75         adj.arr[x][y] = adj.arr[y][x] = 1;
 76     }
 77 
 78     scanf("%d", &nf);
 79     for(int i = 1; i <= nf; ++i) {
 80         scanf("%d", &T[i]);
 81         for(int j = 0; j < T[i]; ++j) {
 82             scanf("%d", &p[i][j]);
 83         }
 84     }
 85 
 86     Mat fac(n, 1), chg[15];
 87     for(int t = 1; t <= 12; ++t) {
 88         chg[t] = adj;
 89         for(int i = 1; i <= nf; ++i) {
 90             chg[t].Dlt(p[i][t % T[i]]);
 91         }
 92         fac = fac * chg[t];
 93     }
 94     fac = qpw(fac, k / 12);
 95     for(int i = 1; i <= k % 12; ++i) {
 96         fac = fac * chg[i];
 97     }
 98 
 99     printf("%d", fac.arr[s][e]);
100 
101     return 0;
102 }
原文地址:https://www.cnblogs.com/qqq1112/p/15043700.html