#include #include #include #include #include #include #include #include #include #include namespace emthrm { template std::vector subset_convolution( const std::vector& f, const std::vector& g) { using Polynomial = std::array; assert(std::has_single_bit(f.size()) && f.size() == g.size()); const int n = std::countr_zero(f.size()); assert(n <= MaxN); const int domain_size = 1 << n; const auto ranked_zeta_transform = [n, domain_size](const std::vector& f) -> std::vector { std::vector a(domain_size, Polynomial{}); for (int i = 0; i < domain_size; ++i) { a[i][std::popcount(static_cast(i))] = f[i]; } for (int bit = 1; bit < domain_size; bit <<= 1) { for (int i = 0; i < domain_size; ++i) { if ((i & bit) == 0) { for (int degree = 0; degree <= n; ++degree) { a[i | bit][degree] += a[i][degree]; } } } } return a; }; std::vector a = ranked_zeta_transform(f); const std::vector b = ranked_zeta_transform(g); for (int i = 0; i < domain_size; ++i) { // Hadamard product for (int degree_of_a = n; degree_of_a >= 0; --degree_of_a) { const T tmp = std::exchange(a[i][degree_of_a], T{}); for (int degree_of_b = 0; degree_of_a + degree_of_b <= n; ++degree_of_b) { a[i][degree_of_a + degree_of_b] += tmp * b[i][degree_of_b]; } } } for (int bit = 1; bit < domain_size; bit <<= 1) { for (int i = 0; i < domain_size; ++i) { if ((i & bit) == 0) { for (int degree = 0; degree <= n; ++degree) { a[i | bit][degree] -= a[i][degree]; } } } } std::vector c(domain_size); for (int i = 0; i < domain_size; ++i) { c[i] = a[i][std::popcount(static_cast(i))]; } return c; } template std::vector exp_of_set_power_series(const std::vector& f) { assert(std::has_single_bit(f.size()) && f[0] == 0); const int n = std::countr_zero(f.size()); assert(n <= MaxN); std::vector exponential{1}; exponential.reserve(1 << n); for (int i = 0; i < n; ++i) { std::ranges::copy(subset_convolution( exponential, std::vector(std::next(f.begin(), 1 << i), std::next(f.begin(), 1 << (i + 1)))), std::back_inserter(exponential)); } return exponential; } } // namespace emthrm bool KthBit(const std::unsigned_integral auto bit_field, const int k) { return std::cmp_equal(bit_field >> k & 1, 1); } int main() { constexpr int kMaxN = 13; int n, m; std::cin >> n >> m; assert(2 <= n && n <= kMaxN && 1 <= m && m <= n * (n - 1) / 2); std::vector graph(n); while (m--) { int u, v; std::cin >> u >> v; assert(1 <= u && u < v && v <= n); --u; --v; graph[u] |= UINT32_C(1) << v; graph[v] |= UINT32_C(1) << u; } // O(2^n n^2) 時間 std::vector num_of_paths(1 << n, std::vector(n, INT64_C(0))); for (int start = 0; start < n; ++start) { num_of_paths[1 << start][start] = 1; } for (std::uint32_t bit = 1; bit < (UINT32_C(1) << n); ++bit) { const int start = std::countr_zero(bit); for (int current_vertex = start; current_vertex < n; ++current_vertex) { for (int next_vertex = start + 1; next_vertex < n; ++next_vertex) { if (!KthBit(bit, next_vertex) && KthBit(graph[current_vertex], next_vertex)) { // 辺が存在する num_of_paths[bit | (1 << next_vertex)][next_vertex] += num_of_paths[bit][current_vertex]; } } } } // O(2^n n) 時間 std::vector num_of_cycles(1 << n, 0); for (int vertex = 0; vertex < n; ++vertex) { num_of_cycles[1 << vertex] = 1; } for (std::uint32_t bit = 7; bit < (UINT32_C(1) << n); ++bit) { if (std::popcount(bit) < 3) continue; const int start = std::countr_zero(bit); for (int last = start + 1; last < n; ++last) { if (KthBit(bit, last) && KthBit(graph[last], start)) { num_of_cycles[bit] += num_of_paths[bit][last]; } } num_of_cycles[bit] /= 2; // 2回数えている } // 誘導部分グラフが条件を満たし、かつ連結な辺の塗り方の個数 std::vector dp(1 << n, 0); dp[1] = 1; for (int vertex = 1; vertex < n; ++vertex) { // 頂点集合を 1 ずつ大きくする // O(3^k k^2) 時間 for (std::uint32_t cycle = UINT32_C(1) << vertex; // vertex を含む閉路 cycle < (UINT32_C(1) << (vertex + 1)); ++cycle) { if (num_of_cycles[cycle] == 0) continue; std::vector on_cycle; on_cycle.reserve(std::popcount(cycle)); for (int i = 0; i <= vertex; ++i) { if (KthBit(cycle, i)) on_cycle.emplace_back(i); } const int size = 1 << (vertex + 1 - on_cycle.size()); std::vector bit_fields; // 閉路以外の頂点集合の部分集合 bit_fields.reserve(size); const std::uint32_t ground_set = ((UINT32_C(1) << (vertex + 1)) - 1) ^ cycle; for (std::uint32_t subset = ground_set; ; subset = (subset - 1) & ground_set) { bit_fields.emplace_back(subset); if (subset == UINT32_C(0)) break; } assert(std::ssize(bit_fields) == size); std::ranges::sort(bit_fields); std::vector tmp(size); for (int i = 0; i < size; ++i) { int degree = 0; for (const int v : on_cycle) { degree += std::popcount(bit_fields[i] & graph[v]); } tmp[i] = dp[bit_fields[i]] * degree; } tmp = emthrm::exp_of_set_power_series(tmp); for (int i = 0; i < size; ++i) { dp[bit_fields[i] | cycle] += tmp[i] * num_of_cycles[cycle]; } } } std::cout << emthrm::exp_of_set_power_series(dp)[(1 << n) - 1] << '\n'; return 0; }