Condition: initial (A) and final (B) vertices, number of vertices (N), edges (M) and graph description are given. It is necessary to find the number of shortest paths from A to B. The graph is not oriented, not weighted (by the long path is meant the number of edges in the path). Restrictions on the input parameters: 1 <= N <10 ^ 5, 0 <= M min (10 ^ 5, N (N-1) / 2). Time limit 1 sec.
The solution is simple: BFS will find the shortest distance and raise the adjacency matrix of this graph to the power of this distance. In the cell [A-1] [B-1] and will be the answer.
But the problem is in speed ... To speed up the exponentiation, I wrote the Strassen algorithm, but it did not help either (unless, of course, I wrote correctly)
Attaching the code and waiting for tips on speeding up (substantial)
#include <iostream> #include <vector> using Graph = std::vector<std::vector<int>>; std::ostream& operator <<(std::ostream &os, const std::vector<std::vector<int>> &other) { for (auto line : other) { for (auto elem : line) { os << elem << " "; } os << std::endl; } return os; } Graph readGraph(int vertex, int edge) { Graph g(vertex); for (int i = 0; i != edge; ++i) { int from, to; std::cin >> from >> to; g[from - 1].push_back(to - 1); g[to - 1].push_back(from - 1); } return g; } std::vector<int> BFS(const Graph &g, int start) { std::vector<int> path(g.size(), -1); path[start] = 0; std::queue<int> q; q.push(start); while (!q.empty()) { auto current = q.front(); q.pop(); for (auto c : g[current]) { if (path[c] == -1) { path[c] = path[current] + 1; q.push(c); } } } return path; } Graph matrixAdj(const Graph &g) { Graph _g(g.size(), std::vector<int>(g.size())); for (int i = 0; i != g.size(); ++i) { for (int j = 0; j != g[i].size(); ++j) { _g[i][g[i][j]] = 1; } } return _g; } Graph matrixMultiply(const Graph &a, const Graph &b) { int size = a.size(); Graph c(size, std::vector<int>(size)); for (size_t i = 0; i != size; ++i) { for (size_t j = 0; j != size; ++j) { c[i][j] = 0; for (size_t k = 0; k != size; ++k) { c[i][j] += a[i][k] * b[k][j]; } } } return c; } Graph operator + (const Graph &a, const Graph &b) { size_t size = a.size(); Graph c(size, std::vector<int>(size)); for (size_t i = 0; i != size; ++i) { for (size_t j = 0; j != size; ++j) { c[i][j] = a[i][j] + b[i][j]; } } return c; } Graph operator - (const Graph &a, const Graph &b) { size_t size = a.size(); Graph c(size, std::vector<int>(size)); for (size_t i = 0; i != size; ++i) { for (size_t j = 0; j != size; ++j) { c[i][j] = a[i][j] - b[i][j]; } } return c; } Graph fastMult(const Graph &a, const Graph &b) { int size = a.size(); Graph c(size, std::vector<int>(size)); if (size == 1) { c[0][0] = a[0][0] * b[0][0]; } else if (size > 64) { size /= 2; Graph a_1(size, std::vector<int>(size)), a_2(size, std::vector<int>(size)), a_3(size, std::vector<int>(size)), a_4(size, std::vector<int>(size)), b_1(size, std::vector<int>(size)), b_2(size, std::vector<int>(size)), b_3(size, std::vector<int>(size)), b_4(size, std::vector<int>(size)), c_1(size, std::vector<int>(size)), c_2(size, std::vector<int>(size)), c_3(size, std::vector<int>(size)), c_4(size, std::vector<int>(size)); for (size_t i = 0; i != size; ++i) { for (size_t j = 0; j != size; ++j) { a_1[i][j] = a[i][j]; a_2[i][j] = a[i][j + size]; a_3[i][j] = a[i + size][j]; a_4[i][j] = a[i + size][j + size]; b_1[i][j] = b[i][j]; b_2[i][j] = b[i][j + size]; b_3[i][j] = b[i + size][j]; b_4[i][j] = b[i + size][j + size]; } } Graph p_1 = fastMult(a_1 + a_4, b_1 + b_4), p_2 = fastMult(a_3 + a_4, b_1), p_3 = fastMult(a_1, b_2 - b_4), p_4 = fastMult(a_4, b_3 - b_1), p_5 = fastMult(a_1 + a_2, b_4), p_6 = fastMult(a_3 - a_1, b_1 + b_2), p_7 = fastMult(a_2 - a_4, b_3 + b_4); c_1 = p_1 + p_4 - p_5 + p_7; c_2 = p_3 + p_5; c_3 = p_2 + p_4; c_4 = p_1 - p_2 + p_3 + p_6; for (size_t i = 0; i != size; ++i) { for (size_t j = 0; j != size; ++j) { c[i][j] = c_1[i][j]; c[i][j + size] = c_2[i][j]; c[i + size][j] = c_3[i][j]; c[i + size][j + size] = c_4[i][j]; } } } else { c = matrixMultiply(a, b); } return c; } int isPow2(int a) { return !(a&(a-1)); } Graph powMatrix(const Graph &a, int power) { int size = a.size(); Graph c = a; for (size_t i = 0; i != power - 1; ++i) { c = fastMult(c, a); } return c; } int main() { int vertex, edge, start, finish; std::cin >> start >> finish >> vertex >> edge; Graph g = readGraph(vertex, edge); auto matrix = matrixAdj(g); if (!(isPow2(vertex))) { while (!(isPow2(vertex))) { ++vertex; } matrix.resize(vertex); for (size_t i = 0; i != vertex; ++i) { if (matrix[i].empty()) { matrix[i] = std::vector<int>(vertex); } else { matrix[i].resize(vertex); } } } auto pathLen = BFS(g, start - 1); int power = pathLen[finish - 1]; matrix = powMatrix(matrix, power); std::cout << matrix[start - 1][finish - 1] << std::endl; return 0; }
log N
- through squaring. But this is all a palliative. We need a fundamentally different algorithm - with such parameters - up to 100,000 - such small movements will not give anything. - Harry