@Dmaxiya
2022-12-04T14:24:41.000000Z
字数 6618
阅读 426
出题
当 时,由于数据范围很小,完全可以暴力深搜计算到达每个节点的方案数,最后将与节点 相邻的方案数相加取余就是答案,时间复杂度为 ,这种做法可以通过这部分数据。
对于统计图中路径方案数,可以先创建一个邻接矩阵 M[i][j](下标从 开始),M[i][j]=M[j][i]=x 表示节点 与节点 之间有 条无向边,注意当 时,M[u][v] 只能被计算一次;由于大禹不能走回家里,只能有以 为起点的边,不能有以 为终点的边,所以 G[i][1] 需要手动清零。
两个邻接矩阵相乘,得到的第 行第 列的值,就是从节点 出发,走两步能到达节点 的方案数,题目要求 步以内的方案数总和,即求以下公式的结果:
当 时,由于 非常大,每次矩阵乘法的时间复杂度都为 ,若使用顺序乘法,总共需要计算 次,必然超时,需要优化。
考虑计算 ,有以下递推式:
当 时, 的数据范围达到 ,上一种解法已不适用,观察到节点 与膜数 的值非常小,考虑计算循环节,由于矩阵 第一列元素全为 ,之后无论如何相乘第一列都必然为 ,因此可以忽略第一列的状态变化。
除去第 列共有 个元素在不断变化,可知循环节长度必然小于等于 ,因此最多只要计算出前 项 即可。
循环节状态必然为:
由于 非常大,但 对 取余前后再对 做减法的结果是一致的,因此可以不必做大数减法计算,先用 对 取余后再对 做减法同样可以得到正确的循环节下标。
时间复杂度为 ,最坏情况下需要执行约 次计算。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 100;int T, n, m, k, p, ans, u, v;int cnt[maxn];bool isDoorway[maxn];vector<int> G[maxn];void dfs(int root, int k) {++cnt[root];if (k == 0) {return ;}for (auto pos: G[root]) {if (pos == 1) {continue;}dfs(pos, k - 1);}}int main() {cin >> T;cin >> n >> m >> k >> p;for (int i = 0; i < m; ++i) {cin >> u >> v;if (u > v) {swap(u, v);}G[u].push_back(v);if (u == 1) {isDoorway[v] = true;} else {if (u != v) {G[v].push_back(u);}}}dfs(1, k);for (int i = 2; i <= n; ++i) {if (isDoorway[i]) {ans += cnt[i];}}cout << ans % p << endl;return 0;}
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int matrixSize = 50;const int maxn = 100000 + 100;int MOD;bool isDoorway[50];LL add(LL a, LL b) {a += b;if(a >= MOD) {return a - MOD;}return a;}struct Matrix {int size;LL num[matrixSize][matrixSize];const Matrix& operator=(const Matrix &m) {size = m.size;for(int i = 0; i < size; ++i) {memcpy(num[i], m.num[i], sizeof(LL) * size);}return (*this);}void init() {for(int i = 0; i < size; ++i) {memset(num[i], 0, sizeof(LL) * size);num[i][i] = 1;}}void clear() {for (int i = 0; i < size; ++i) {memset(num[i], 0, sizeof(LL) * size);}}void set(int n, int m) {size = n;int u, v;for (int i = 0; i < m; ++i) {scanf("%d%d", &u, &v);--u;--v;++num[u][v];if (u != v) {++num[v][u];}}for (int i = 0; i < size; ++i) {for (int j = 0; j < size; ++j) {if (j == 0) {num[i][j] = 0;} else {if (i == 0) {isDoorway[j] = (num[i][j] != 0);}num[i][j] %= MOD;}}}}const Matrix& operator*(const Matrix &m) const {static Matrix ans;ans.size = m.size;for(int i = 0; i < size; ++i) {for(int j = 0; j < size; ++j) {ans.num[i][j] = 0;for(int k = 0; k < size; ++k) {ans.num[i][j] = add(ans.num[i][j], num[i][k] * m.num[k][j] % MOD);}}}return ans;}const Matrix& operator*=(const Matrix &m) {(*this) = (*this) * m;return *this;}const Matrix& operator+(const Matrix &m) const {static Matrix ans;ans.size = m.size;for (int i = 0; i < size; ++i) {for (int j = 0; j < size; ++j) {ans.num[i][j] = add(num[i][j], m.num[i][j]);}}return ans;}const Matrix& operator+=(const Matrix &m) {for (int i = 0; i < size; ++i) {for (int j = 0; j < size; ++j) {num[i][j] = add(num[i][j], m.num[i][j]);}}return *this;}LL hash() {LL ret = 0;for (int i = 0; i < size; ++i) {for (int j = 1; j < size; ++j) {ret = ret * 10 + num[i][j];}}return ret;}};Matrix one, zero;struct MMatrix {int size;Matrix m[2][2];void set(const Matrix &m) {size = 2;this->m[0][0] = m;this->m[0][1] = one;this->m[1][0] = zero;this->m[1][1] = one;}const MMatrix& operator=(const MMatrix &mm) {size = mm.size;for(int i = 0; i < size; ++i) {for (int j = 0; j < size; ++j) {m[i][j] = mm.m[i][j];}}return (*this);}void init() {for(int i = 0; i < size; ++i) {m[i][i] = one;m[i][i ^ 1] = zero;}}const MMatrix& operator*=(const MMatrix &mm) {static MMatrix ans;ans.size = size;for(int i = 0; i < size; ++i) {for(int j = 0; j < size; ++j) {ans.m[i][j] = zero;for(int k = 0; k < size; ++k) {ans.m[i][j] += m[i][k] * mm.m[k][j];}}}(*this) = ans;return ans;}void fastPow(LL n) {static MMatrix ans;ans.size = size;for(ans.init(); n != 0; n >>= 1) {if((n & 1) == 1) {ans *= (*this);}(*this) *= (*this);}(*this) = ans;}};int T, n, m, k, ans;Matrix matrix, matrixSum;MMatrix mmatrix;char kStr[maxn];LL h;LL pow10[20];map<LL, int> hashIndex;vector<LL> hs;int getNum(LL h, int x, int y) {int r = n - 1 - x;int c = n - 1 - y;return h / pow10[r * (n - 1) + c] % 10;}void solve1() {scanf("%d%d%d%d", &n, &m, &k, &MOD);matrix.set(n, m);one.size = n;one.init();zero.size = n;zero.clear();mmatrix.set(matrix);mmatrix.fastPow(k);mmatrix.m[0][1] *= matrix;ans = 0;for (int i = 1; i < n; ++i) {if (isDoorway[i]) {ans = (ans + mmatrix.m[0][1].num[0][i]) % MOD;}}printf("%d\n", ans);}void solve2() {pow10[0] = 1;for (int i = 1; i < 18; ++i) {pow10[i] = pow10[i - 1] * 10;}scanf("%d%d%s%d", &n, &m, kStr, &MOD);matrix.set(n, m);matrixSum.size = n;matrixSum.clear();h = matrixSum.hash();do {hashIndex[h] = hs.size();hs.push_back(h);matrixSum = matrixSum * matrix + matrix;h = matrixSum.hash();} while (hashIndex.find(h) == hashIndex.end());int loopIndex = hashIndex[h];int loopLength = hs.size() - loopIndex;int index = 0;for (int i = 0; kStr[i] != '\0'; ++i) {index = (index * 10 + (kStr[i] - '0')) % loopLength;}index = ((index - loopIndex) % loopLength + loopLength) % loopLength + loopIndex;for (int j = 1; j < n; ++j) {if (isDoorway[j]) {ans = (ans + getNum(hs[index], 0, j)) % MOD;}}printf("%d\n", ans);}int main() {scanf("%d", &T);if (T <= 30) {solve1();return 0;}solve2();return 0;}
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int testCases = 50;ofstream fout;int T, n, m, k, p, u, v;string numToStr(int x) {string str;while (x != 0) {str = char('0' + x % 10) + str;x /= 10;}return str;}string getFileName(int index) {return "data/" + numToStr(index) + ".in";}int getRand(int l, int r) {return (LL)rand() * rand() * rand() % (r - l + 1) + l;}void writeToFile() {for (int i = 0; i < m; ++i) {u = getRand(1, n);v = getRand(1, n);fout << u << " " << v << endl;}}int main() {srand(time(0));// data in 20%, 2 <= n,m,k <= 7, p = 1e9+7int one = testCases * 20 / 100;for (int i = 1; i <= one; ++i) {fout.open(getFileName(i));fout << i << endl;n = getRand(5, 7);m = getRand(5, 7);k = getRand(5, 7);p = 1000000007;fout << n << " " << m << " " << k << " " << p << endl;writeToFile();fout.close();}// data in 60%, 2 <= n <= 50, 1 <= m <= 1e6, 1 <= k <= 1e6, 1e7 <= p <= 1e9int two = testCases * 60 / 100;for (int i = one + 1; i <= two; ++i) {fout.open(getFileName(i));fout << i << endl;n = getRand(45, 50);m = getRand(100000, 1000000);k = getRand(999900, 1000000);p = getRand(10000000, 1000000000);fout << n << " " << m << " " << k << " " << p << endl;writeToFile();fout.close();}// data in 100%, 2 <= n <= 4, 1 <= m <= 1e6, 1 <= k <= 1e100000, 1 <= p <= 3for (int i = two + 1; i <= testCases; ++i) {fout.open(getFileName(i));fout << i << endl;n = getRand(3, 4);m = getRand(100000, 1000000);p = getRand(2, 3);int lenOfk = getRand(50000, 100000);fout << n << " " << m << " ";fout << getRand(1, 9);for (int j = 1; j < lenOfk; ++j) {fout << getRand(0, 9);}fout << " " << p << endl;writeToFile();fout.close();}return 0;}