結果
| 問題 |
No.2507 Yet Another Subgraph Counting
|
| コンテスト | |
| ユーザー |
emthrm
|
| 提出日時 | 2023-08-27 17:05:58 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 7,227 bytes |
| コンパイル時間 | 1,856 ms |
| コンパイル使用メモリ | 121,648 KB |
| 実行使用メモリ | 10,496 KB |
| 最終ジャッジ日時 | 2024-12-27 20:45:26 |
| 合計ジャッジ時間 | 17,738 ms |
|
ジャッジサーバーID (参考情報) |
judge3 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 48 TLE * 4 |
ソースコード
#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;
}
emthrm