[关闭]
@Dmaxiya 2022-12-04T22:24:41.000000Z 字数 6618 阅读 215

大禹治水题解

出题


时,由于数据范围很小,完全可以暴力深搜计算到达每个节点的方案数,最后将与节点 相邻的方案数相加取余就是答案,时间复杂度为 ,这种做法可以通过这部分数据。


对于统计图中路径方案数,可以先创建一个邻接矩阵 M[i][j](下标从 开始),M[i][j]=M[j][i]=x 表示节点 与节点 之间有 条无向边,注意当 时,M[u][v] 只能被计算一次;由于大禹不能走回家里,只能有以 为起点的边,不能有以 为终点的边,所以 G[i][1] 需要手动清零。

两个邻接矩阵相乘,得到的第 行第 列的值,就是从节点 出发,走两步能到达节点 的方案数,题目要求 步以内的方案数总和,即求以下公式的结果:


取结果矩阵的第 行所有最初与节点 相邻的节点位置的方案数总和,即为答案。


时,由于 非常大,每次矩阵乘法的时间复杂度都为 ,若使用顺序乘法,总共需要计算 次,必然超时,需要优化。

考虑计算 ,有以下递推式:


其中 为单位矩阵, 为邻接矩阵,将其转化为矩阵运算(矩阵内每一项都是一个 的矩阵):

由于 次幂的每一项都相同,且矩阵乘法满足结合律,因此可以使用矩阵快速幂在 次内计算出矩阵 ,该解法的时间复杂度为


时, 的数据范围达到 ,上一种解法已不适用,观察到节点 与膜数 的值非常小,考虑计算循环节,由于矩阵 第一列元素全为 ,之后无论如何相乘第一列都必然为 ,因此可以忽略第一列的状态变化。

除去第 列共有 个元素在不断变化,可知循环节长度必然小于等于 ,因此最多只要计算出前 即可。

循环节状态必然为:


此时循环节长度为 ,若 ,则直接对应 的结果,若 ,则 将对应到 的结果。

由于 非常大,但 取余前后再对 做减法的结果是一致的,因此可以不必做大数减法计算,先用 取余后再对 做减法同样可以得到正确的循环节下标。

时间复杂度为 ,最坏情况下需要执行约 次计算。

暴力解法

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn = 100;
  5. int T, n, m, k, p, ans, u, v;
  6. int cnt[maxn];
  7. bool isDoorway[maxn];
  8. vector<int> G[maxn];
  9. void dfs(int root, int k) {
  10. ++cnt[root];
  11. if (k == 0) {
  12. return ;
  13. }
  14. for (auto pos: G[root]) {
  15. if (pos == 1) {
  16. continue;
  17. }
  18. dfs(pos, k - 1);
  19. }
  20. }
  21. int main() {
  22. cin >> T;
  23. cin >> n >> m >> k >> p;
  24. for (int i = 0; i < m; ++i) {
  25. cin >> u >> v;
  26. if (u > v) {
  27. swap(u, v);
  28. }
  29. G[u].push_back(v);
  30. if (u == 1) {
  31. isDoorway[v] = true;
  32. } else {
  33. if (u != v) {
  34. G[v].push_back(u);
  35. }
  36. }
  37. }
  38. dfs(1, k);
  39. for (int i = 2; i <= n; ++i) {
  40. if (isDoorway[i]) {
  41. ans += cnt[i];
  42. }
  43. }
  44. cout << ans % p << endl;
  45. return 0;
  46. }

标程

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int matrixSize = 50;
  5. const int maxn = 100000 + 100;
  6. int MOD;
  7. bool isDoorway[50];
  8. LL add(LL a, LL b) {
  9. a += b;
  10. if(a >= MOD) {
  11. return a - MOD;
  12. }
  13. return a;
  14. }
  15. struct Matrix {
  16. int size;
  17. LL num[matrixSize][matrixSize];
  18. const Matrix& operator=(const Matrix &m) {
  19. size = m.size;
  20. for(int i = 0; i < size; ++i) {
  21. memcpy(num[i], m.num[i], sizeof(LL) * size);
  22. }
  23. return (*this);
  24. }
  25. void init() {
  26. for(int i = 0; i < size; ++i) {
  27. memset(num[i], 0, sizeof(LL) * size);
  28. num[i][i] = 1;
  29. }
  30. }
  31. void clear() {
  32. for (int i = 0; i < size; ++i) {
  33. memset(num[i], 0, sizeof(LL) * size);
  34. }
  35. }
  36. void set(int n, int m) {
  37. size = n;
  38. int u, v;
  39. for (int i = 0; i < m; ++i) {
  40. scanf("%d%d", &u, &v);
  41. --u;
  42. --v;
  43. ++num[u][v];
  44. if (u != v) {
  45. ++num[v][u];
  46. }
  47. }
  48. for (int i = 0; i < size; ++i) {
  49. for (int j = 0; j < size; ++j) {
  50. if (j == 0) {
  51. num[i][j] = 0;
  52. } else {
  53. if (i == 0) {
  54. isDoorway[j] = (num[i][j] != 0);
  55. }
  56. num[i][j] %= MOD;
  57. }
  58. }
  59. }
  60. }
  61. const Matrix& operator*(const Matrix &m) const {
  62. static Matrix ans;
  63. ans.size = m.size;
  64. for(int i = 0; i < size; ++i) {
  65. for(int j = 0; j < size; ++j) {
  66. ans.num[i][j] = 0;
  67. for(int k = 0; k < size; ++k) {
  68. ans.num[i][j] = add(ans.num[i][j], num[i][k] * m.num[k][j] % MOD);
  69. }
  70. }
  71. }
  72. return ans;
  73. }
  74. const Matrix& operator*=(const Matrix &m) {
  75. (*this) = (*this) * m;
  76. return *this;
  77. }
  78. const Matrix& operator+(const Matrix &m) const {
  79. static Matrix ans;
  80. ans.size = m.size;
  81. for (int i = 0; i < size; ++i) {
  82. for (int j = 0; j < size; ++j) {
  83. ans.num[i][j] = add(num[i][j], m.num[i][j]);
  84. }
  85. }
  86. return ans;
  87. }
  88. const Matrix& operator+=(const Matrix &m) {
  89. for (int i = 0; i < size; ++i) {
  90. for (int j = 0; j < size; ++j) {
  91. num[i][j] = add(num[i][j], m.num[i][j]);
  92. }
  93. }
  94. return *this;
  95. }
  96. LL hash() {
  97. LL ret = 0;
  98. for (int i = 0; i < size; ++i) {
  99. for (int j = 1; j < size; ++j) {
  100. ret = ret * 10 + num[i][j];
  101. }
  102. }
  103. return ret;
  104. }
  105. };
  106. Matrix one, zero;
  107. struct MMatrix {
  108. int size;
  109. Matrix m[2][2];
  110. void set(const Matrix &m) {
  111. size = 2;
  112. this->m[0][0] = m;
  113. this->m[0][1] = one;
  114. this->m[1][0] = zero;
  115. this->m[1][1] = one;
  116. }
  117. const MMatrix& operator=(const MMatrix &mm) {
  118. size = mm.size;
  119. for(int i = 0; i < size; ++i) {
  120. for (int j = 0; j < size; ++j) {
  121. m[i][j] = mm.m[i][j];
  122. }
  123. }
  124. return (*this);
  125. }
  126. void init() {
  127. for(int i = 0; i < size; ++i) {
  128. m[i][i] = one;
  129. m[i][i ^ 1] = zero;
  130. }
  131. }
  132. const MMatrix& operator*=(const MMatrix &mm) {
  133. static MMatrix ans;
  134. ans.size = size;
  135. for(int i = 0; i < size; ++i) {
  136. for(int j = 0; j < size; ++j) {
  137. ans.m[i][j] = zero;
  138. for(int k = 0; k < size; ++k) {
  139. ans.m[i][j] += m[i][k] * mm.m[k][j];
  140. }
  141. }
  142. }
  143. (*this) = ans;
  144. return ans;
  145. }
  146. void fastPow(LL n) {
  147. static MMatrix ans;
  148. ans.size = size;
  149. for(ans.init(); n != 0; n >>= 1) {
  150. if((n & 1) == 1) {
  151. ans *= (*this);
  152. }
  153. (*this) *= (*this);
  154. }
  155. (*this) = ans;
  156. }
  157. };
  158. int T, n, m, k, ans;
  159. Matrix matrix, matrixSum;
  160. MMatrix mmatrix;
  161. char kStr[maxn];
  162. LL h;
  163. LL pow10[20];
  164. map<LL, int> hashIndex;
  165. vector<LL> hs;
  166. int getNum(LL h, int x, int y) {
  167. int r = n - 1 - x;
  168. int c = n - 1 - y;
  169. return h / pow10[r * (n - 1) + c] % 10;
  170. }
  171. void solve1() {
  172. scanf("%d%d%d%d", &n, &m, &k, &MOD);
  173. matrix.set(n, m);
  174. one.size = n;
  175. one.init();
  176. zero.size = n;
  177. zero.clear();
  178. mmatrix.set(matrix);
  179. mmatrix.fastPow(k);
  180. mmatrix.m[0][1] *= matrix;
  181. ans = 0;
  182. for (int i = 1; i < n; ++i) {
  183. if (isDoorway[i]) {
  184. ans = (ans + mmatrix.m[0][1].num[0][i]) % MOD;
  185. }
  186. }
  187. printf("%d\n", ans);
  188. }
  189. void solve2() {
  190. pow10[0] = 1;
  191. for (int i = 1; i < 18; ++i) {
  192. pow10[i] = pow10[i - 1] * 10;
  193. }
  194. scanf("%d%d%s%d", &n, &m, kStr, &MOD);
  195. matrix.set(n, m);
  196. matrixSum.size = n;
  197. matrixSum.clear();
  198. h = matrixSum.hash();
  199. do {
  200. hashIndex[h] = hs.size();
  201. hs.push_back(h);
  202. matrixSum = matrixSum * matrix + matrix;
  203. h = matrixSum.hash();
  204. } while (hashIndex.find(h) == hashIndex.end());
  205. int loopIndex = hashIndex[h];
  206. int loopLength = hs.size() - loopIndex;
  207. int index = 0;
  208. for (int i = 0; kStr[i] != '\0'; ++i) {
  209. index = (index * 10 + (kStr[i] - '0')) % loopLength;
  210. }
  211. index = ((index - loopIndex) % loopLength + loopLength) % loopLength + loopIndex;
  212. for (int j = 1; j < n; ++j) {
  213. if (isDoorway[j]) {
  214. ans = (ans + getNum(hs[index], 0, j)) % MOD;
  215. }
  216. }
  217. printf("%d\n", ans);
  218. }
  219. int main() {
  220. scanf("%d", &T);
  221. if (T <= 30) {
  222. solve1();
  223. return 0;
  224. }
  225. solve2();
  226. return 0;
  227. }

数据生成

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int testCases = 50;
  5. ofstream fout;
  6. int T, n, m, k, p, u, v;
  7. string numToStr(int x) {
  8. string str;
  9. while (x != 0) {
  10. str = char('0' + x % 10) + str;
  11. x /= 10;
  12. }
  13. return str;
  14. }
  15. string getFileName(int index) {
  16. return "data/" + numToStr(index) + ".in";
  17. }
  18. int getRand(int l, int r) {
  19. return (LL)rand() * rand() * rand() % (r - l + 1) + l;
  20. }
  21. void writeToFile() {
  22. for (int i = 0; i < m; ++i) {
  23. u = getRand(1, n);
  24. v = getRand(1, n);
  25. fout << u << " " << v << endl;
  26. }
  27. }
  28. int main() {
  29. srand(time(0));
  30. // data in 20%, 2 <= n,m,k <= 7, p = 1e9+7
  31. int one = testCases * 20 / 100;
  32. for (int i = 1; i <= one; ++i) {
  33. fout.open(getFileName(i));
  34. fout << i << endl;
  35. n = getRand(5, 7);
  36. m = getRand(5, 7);
  37. k = getRand(5, 7);
  38. p = 1000000007;
  39. fout << n << " " << m << " " << k << " " << p << endl;
  40. writeToFile();
  41. fout.close();
  42. }
  43. // data in 60%, 2 <= n <= 50, 1 <= m <= 1e6, 1 <= k <= 1e6, 1e7 <= p <= 1e9
  44. int two = testCases * 60 / 100;
  45. for (int i = one + 1; i <= two; ++i) {
  46. fout.open(getFileName(i));
  47. fout << i << endl;
  48. n = getRand(45, 50);
  49. m = getRand(100000, 1000000);
  50. k = getRand(999900, 1000000);
  51. p = getRand(10000000, 1000000000);
  52. fout << n << " " << m << " " << k << " " << p << endl;
  53. writeToFile();
  54. fout.close();
  55. }
  56. // data in 100%, 2 <= n <= 4, 1 <= m <= 1e6, 1 <= k <= 1e100000, 1 <= p <= 3
  57. for (int i = two + 1; i <= testCases; ++i) {
  58. fout.open(getFileName(i));
  59. fout << i << endl;
  60. n = getRand(3, 4);
  61. m = getRand(100000, 1000000);
  62. p = getRand(2, 3);
  63. int lenOfk = getRand(50000, 100000);
  64. fout << n << " " << m << " ";
  65. fout << getRand(1, 9);
  66. for (int j = 1; j < lenOfk; ++j) {
  67. fout << getRand(0, 9);
  68. }
  69. fout << " " << p << endl;
  70. writeToFile();
  71. fout.close();
  72. }
  73. return 0;
  74. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注