結果
問題 | No.2507 Yet Another Subgraph Counting |
ユーザー | 👑 emthrm |
提出日時 | 2023-08-27 17:05:58 |
言語 | C++23 (gcc 12.3.0 + boost 1.83.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 7,227 bytes |
コンパイル時間 | 2,111 ms |
コンパイル使用メモリ | 121,904 KB |
実行使用メモリ | 11,680 KB |
最終ジャッジ日時 | 2024-06-08 12:53:51 |
合計ジャッジ時間 | 7,614 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge5 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 1 ms
10,752 KB |
testcase_01 | AC | 2 ms
5,248 KB |
testcase_02 | AC | 2 ms
5,248 KB |
testcase_03 | AC | 2 ms
5,376 KB |
testcase_04 | AC | 2 ms
5,376 KB |
testcase_05 | AC | 3 ms
5,376 KB |
testcase_06 | AC | 2 ms
5,376 KB |
testcase_07 | AC | 2 ms
5,376 KB |
testcase_08 | AC | 1 ms
5,376 KB |
testcase_09 | AC | 1 ms
5,376 KB |
testcase_10 | AC | 1 ms
5,376 KB |
testcase_11 | AC | 2 ms
5,376 KB |
testcase_12 | AC | 2 ms
5,376 KB |
testcase_13 | AC | 1 ms
5,376 KB |
testcase_14 | AC | 3 ms
5,376 KB |
testcase_15 | AC | 1 ms
5,376 KB |
testcase_16 | AC | 2 ms
5,376 KB |
testcase_17 | AC | 3 ms
5,376 KB |
testcase_18 | AC | 1 ms
5,376 KB |
testcase_19 | AC | 2 ms
5,376 KB |
testcase_20 | AC | 1 ms
5,376 KB |
testcase_21 | AC | 1 ms
5,376 KB |
testcase_22 | AC | 3 ms
5,376 KB |
testcase_23 | AC | 36 ms
5,376 KB |
testcase_24 | AC | 5 ms
5,376 KB |
testcase_25 | AC | 20 ms
5,376 KB |
testcase_26 | AC | 414 ms
5,376 KB |
testcase_27 | AC | 16 ms
5,376 KB |
testcase_28 | AC | 46 ms
5,376 KB |
testcase_29 | AC | 2 ms
5,376 KB |
testcase_30 | AC | 390 ms
5,376 KB |
testcase_31 | AC | 11 ms
5,608 KB |
testcase_32 | AC | 12 ms
5,608 KB |
testcase_33 | AC | 3 ms
5,376 KB |
testcase_34 | AC | 2 ms
5,376 KB |
testcase_35 | AC | 2 ms
5,376 KB |
testcase_36 | AC | 2 ms
5,376 KB |
testcase_37 | AC | 945 ms
5,624 KB |
testcase_38 | TLE | - |
testcase_39 | -- | - |
testcase_40 | -- | - |
testcase_41 | -- | - |
testcase_42 | -- | - |
testcase_43 | -- | - |
testcase_44 | -- | - |
testcase_45 | -- | - |
testcase_46 | -- | - |
testcase_47 | -- | - |
testcase_48 | -- | - |
testcase_49 | -- | - |
testcase_50 | -- | - |
testcase_51 | -- | - |
ソースコード
#include <algorithm> #include <array> #include <bit> #include <cassert> #include <concepts> #include <cstdint> #include <iostream> #include <iterator> #include <utility> #include <vector> namespace emthrm { template <int MaxN, typename T> std::vector<T> subset_convolution( const std::vector<T>& f, const std::vector<T>& g) { using Polynomial = std::array<T, MaxN + 1>; 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<T>& f) -> std::vector<Polynomial> { std::vector a(domain_size, Polynomial{}); for (int i = 0; i < domain_size; ++i) { a[i][std::popcount(static_cast<unsigned int>(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<Polynomial> a = ranked_zeta_transform(f); const std::vector<Polynomial> 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<T> c(domain_size); for (int i = 0; i < domain_size; ++i) { c[i] = a[i][std::popcount(static_cast<unsigned int>(i))]; } return c; } template <int MaxN, typename T> std::vector<T> exp_of_set_power_series(const std::vector<T>& f) { assert(std::has_single_bit(f.size()) && f[0] == 0); const int n = std::countr_zero(f.size()); assert(n <= MaxN); std::vector<T> exponential{1}; exponential.reserve(1 << n); for (int i = 0; i < n; ++i) { std::ranges::copy(subset_convolution<MaxN>( 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); } // <TLE> // 閉路集合(頂点のみ)を固定し、縮約したグラフで森を数える。 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<int> u(m), v(m); for (int i = 0; i < m; ++i) { std::cin >> u[i] >> v[i]; assert(1 <= u[i] && u[i] < v[i] && v[i] <= n); --u[i]; --v[i]; } std::vector<std::vector<int>> graph(n); for (int i = 0; i < m; ++i) { graph[u[i]].emplace_back(v[i]); graph[v[i]].emplace_back(u[i]); } 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 (const int next_vertex : graph[current_vertex]) { if (next_vertex > start && !KthBit(bit, next_vertex)) { num_of_paths[bit | (1 << next_vertex)][next_vertex] += num_of_paths[bit][current_vertex]; } } } } std::vector<std::int64_t> 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 (const int last : graph[start]) { if (KthBit(bit, last)) num_of_cycles[bit] += num_of_paths[bit][last]; } num_of_cycles[bit] /= 2; } const auto Solve = [n, m, &graph, &num_of_cycles]( auto Solve, const int vertex, std::uint32_t not_decided, const int num_of_vertices, std::vector<int>* id, std::vector<std::vector<int>>* new_graph) -> std::int64_t { if (vertex == n) { // ref. https://atcoder.jp/contests/abc253/editorial/4028 std::vector<std::uint64_t> num_of_trees{0, 1}; num_of_trees.reserve(1 << num_of_vertices); for (int i = 1; i < num_of_vertices; ++i) { std::vector<std::uint64_t> tmp = num_of_trees; for (std::uint32_t bit_field = 0; bit_field < (1 << i); ++bit_field) { int degree = 0; for (int j = 0; j < i; ++j) { if (KthBit(bit_field, j)) degree += (*new_graph)[i][j]; } tmp[bit_field] *= degree; } std::ranges::copy(emthrm::exp_of_set_power_series<kMaxN>(tmp), std::back_inserter(num_of_trees)); } // https://atcoder.jp/contests/abc236/editorial/3910 で高速化できるらしい return emthrm::exp_of_set_power_series<kMaxN>(num_of_trees).back(); } if ((*id)[vertex] != -1) { return Solve( Solve, vertex + 1, not_decided, num_of_vertices, id, new_graph); } not_decided ^= UINT32_C(1) << vertex; std::int64_t ans = 0; for (std::uint32_t cycle = not_decided; ; cycle = (cycle - 1) & not_decided) { if (num_of_cycles[cycle | (UINT32_C(1) << vertex)] > 0) { std::vector<int> on_cycle{vertex}; on_cycle.reserve(std::popcount(cycle) + 1); for (int i = vertex + 1; i < n; ++i) { if (KthBit(cycle, i)) on_cycle.emplace_back(i); } for (const int v : on_cycle) (*id)[v] = num_of_vertices; for (const int v : on_cycle) { for (const int adj : graph[v]) { if ((*id)[adj] != -1 && (*id)[adj] != num_of_vertices) { ++(*new_graph)[(*id)[adj]][num_of_vertices]; ++(*new_graph)[num_of_vertices][(*id)[adj]]; } } } ans += num_of_cycles[cycle | (UINT32_C(1) << vertex)] * Solve(Solve, vertex + 1, not_decided ^ cycle, num_of_vertices + 1, id, new_graph); for (const int v : on_cycle) { for (const int adj : graph[v]) { if ((*id)[adj] != -1 && (*id)[adj] != num_of_vertices) { --(*new_graph)[(*id)[adj]][num_of_vertices]; --(*new_graph)[num_of_vertices][(*id)[adj]]; } } } for (const int v : on_cycle) (*id)[v] = -1; } if (cycle == 0) break; } return ans; }; std::vector<int> id(n, -1); std::vector new_graph(n, std::vector(n, 0)); std::cout << Solve(Solve, 0, (UINT32_C(1) << n) - 1, 0, &id, &new_graph) << '\n'; return 0; }