結果
| 問題 |
No.2507 Yet Another Subgraph Counting
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2023-08-31 20:31:02 |
| 言語 | C++17(gcc12) (gcc 12.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 83 ms / 2,000 ms |
| コード長 | 10,932 bytes |
| コンパイル時間 | 5,690 ms |
| コンパイル使用メモリ | 237,756 KB |
| 最終ジャッジ日時 | 2025-02-16 15:50:20 |
|
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 52 |
ソースコード
// TLE O(4^n n^2)
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <limits>
#include <utility>
#include <vector>
#ifdef _MSC_VER
# include <intrin.h>
#else
# include <x86intrin.h>
#endif
constexpr size_t N_MAX = 13;
namespace library {
namespace bits {
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
T kth_bit(T v, size_t k) { return (v >> k) & 1; }
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
size_t bit_length(const T v) {
if constexpr (std::numeric_limits<std::make_unsigned_t<T>>::digits <= 32) {
return 32 - __builtin_clz(v);
} else {
return 64 - __builtin_clzll(v);
}
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
size_t popcount(const T v) {
if constexpr (std::numeric_limits<std::make_unsigned_t<T>>::digits <= 32) {
return __builtin_popcount(v);
} else {
return __builtin_popcountll(v);
}
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
size_t count_tz(const T v) {
if constexpr (std::numeric_limits<std::make_unsigned_t<T>>::digits <= 32) {
return __builtin_ctz(v);
} else {
return __builtin_ctzll(v);
}
}
// See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_pdep&ig_expand=4939
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
__attribute__((target("bmi2"))) T pdep(const T src, const T mask) {
/*
T dst = 0;
for (size_t i = 0, j = 0; i < BIT_NUM; ++i) {
if (kth_bit(mask, i)) dst |= kth_bit(src, j) << i, ++j;
}
return dst;
*/
if constexpr (std::numeric_limits<std::make_unsigned_t<T>>::digits <= 32) {
return _pdep_u32(src, mask);
} else {
return _pdep_u64(src, mask);
}
}
}
namespace subset_transform {
template <typename T> void zeta(std::vector<T>& x) {
const size_t n = x.size();
for (size_t b = 1; b < n; b *= 2) for (size_t l = 0; l < n; l += 2 * b) for (size_t i = l; i < l + b; ++i) {
x[i + 1 * b] += x[i + 0 * b];
}
}
template <typename T> void mobius(std::vector<T>& x) {
const size_t n = x.size();
for (size_t b = 1; b < n; b *= 2) for (size_t l = 0; l < n; l += 2 * b) for (size_t i = l; i < l + b; ++i) {
x[i + 1 * b] -= x[i + 0 * b];
}
}
}
namespace set_power_series {
namespace details {
struct polynomial {
polynomial() : polynomial(0) {}
explicit polynomial(size_t n, uint64_t value = {}) { assign(n, value); }
size_t size() const { return _size; }
uint64_t& operator[](size_t i) { return _data[i]; }
const uint64_t& operator[](size_t i) const { return _data[i]; }
void assign(size_t n, uint64_t v) {
_size = std::min(n, N_MAX + 1);
std::fill(_data.begin(), _data.begin() + _size, v);
}
void resize(size_t n, uint64_t v) {
size_t nxt_size = std::min(n, N_MAX + 1);
if (nxt_size > _size) {
std::fill(_data.begin() + _size, _data.begin() + nxt_size, v);
_size = nxt_size;
}
}
void push_front(uint64_t v) {
uint64_t back = _data[_size - 1];
for (size_t i = _size; i --> 1;) {
_data[i] = _data[i - 1];
}
_data[0] = v;
if (_size != N_MAX + 1) {
_data[_size++] = back;
}
}
polynomial& operator+=(const polynomial& q) {
const size_t n = size();
for (size_t i = 0; i < q.size(); ++i) (*this)[i] += q[i];
return *this;
}
polynomial& operator-=(const polynomial& q) {
const size_t n = this->size();
for (size_t i = 0; i < q.size(); ++i) (*this)[i] -= q[i];
return *this;
}
polynomial& operator*=(const polynomial& q) {
const size_t n = size(), m = q.size();
resize(n + m - 1, 0);
const size_t k = size();
for (size_t i = n; i --> 0;) {
const uint64_t v = std::exchange((*this)[i], 0);
const size_t jmax = std::min(k - i, m);
for (size_t j = 0; j < jmax; ++j) {
(*this)[i + j] += v * q[j];
}
}
return *this;
}
polynomial& muleq(const polynomial& q, size_t siz) {
const size_t n = size(), m = q.size();
resize(std::min(siz, n + m - 1), 0);
const size_t k = size();
for (size_t i = n; i --> 0;) {
const uint64_t v = std::exchange((*this)[i], 0);
const size_t jmax = std::min(k - i, m);
for (size_t j = 0; j < jmax; ++j) {
(*this)[i + j] += v * q[j];
}
}
return *this;
}
private:
std::array<uint64_t, N_MAX + 1> _data;
size_t _size;
};
template <typename InputIterator>
std::vector<polynomial> add_rank(InputIterator first, InputIterator last) {
const size_t n = last - first;
std::vector<polynomial> fs(n);
for (size_t i = 0; i < n; ++i) {
size_t popc = bits::popcount(i);
fs[i].assign(popc + 1, 0);
fs[i][popc] = first[i];
}
return fs;
}
std::vector<polynomial> add_rank(const std::vector<uint64_t> &a) {
return add_rank(a.begin(), a.end());
}
std::vector<uint64_t> remove_rank(const std::vector<polynomial>& polys) {
const size_t n = polys.size();
std::vector<uint64_t> a(n);
for (size_t i = 0; i < n; ++i) a[i] = polys[i][bits::popcount(i)];
return a;
}
}
std::vector<uint64_t> subset_exp(const std::vector<uint64_t>& f) {
assert(f[0] == 0);
const size_t n = bits::bit_length(f.size()) - 1;
auto rf = details::add_rank({ 1 });
rf.reserve(size_t(1) << n);
for (size_t i = 0; i < n; ++i) {
auto rg = details::add_rank(f.begin() + (1 << i), f.begin() + (1 << (i + 1)));
subset_transform::zeta(rg);
for (size_t j = 0; j < size_t(1) << i; ++j) {
rg[j].push_front(1);
rg[j].muleq(rf[j], i + 2);
rf.push_back(rg[j]);
}
}
subset_transform::mobius(rf);
return details::remove_rank(rf);
}
}
}
using vertex = size_t;
using vertex_set = size_t;
using edge = std::pair<vertex, vertex>;
using Int = uint64_t;
using set_power_series = std::vector<Int>;
std::vector<Int> count_cycles(const size_t n, const size_t, const std::vector<edge>& edges) {
// adjacency list
std::vector adj(n, std::vector<vertex>{});
for (const auto& [u, v] : edges) adj[u].push_back(v), adj[v].push_back(u);
// "c" mentioned in the editorial
std::vector<Int> c(1u << n);
// dp[S: vertex set][v: vertex] := # simple paths from min S to v passing vertices in S (but not passing vertices not in S)
std::vector dp(1u << n, std::vector<Int>(n));
// base cases
for (vertex v = 0; v < n; ++v) {
dp[1u << v][v] = 1;
}
for (vertex_set S = 1; S < 1u << n; ++S) {
// min S
const vertex start = library::bits::count_tz(S);
for (vertex cur = 0; cur < n; ++cur) for (const vertex nxt : adj[cur]) {
if (start == nxt) {
c[S] += dp[S][cur];
} else if (start < nxt and not library::bits::kth_bit(S, nxt)) {
const vertex_set T = S | (1u << nxt);
dp[T][nxt] += dp[S][cur];
}
}
}
for (vertex_set S = 1; S < 1u << n; ++S) {
const size_t card = library::bits::popcount(S);
if (card == 1) c[S] = 1;
if (card == 2) c[S] = 0;
if (card >= 3) c[S] /= 2;
}
return c;
}
Int solve(const size_t n, const size_t m, const std::vector<edge>& edges) {
// E[S: vertex set] := # edges connecting vertices in S.
std::vector<Int> E(1u << n);
for (const auto& [u, v] : edges) ++E[(1u << u) | (1u << v)];
library::subset_transform::zeta(E);
// "c" mentioned in the editorial
const set_power_series c = count_cycles(n, m, edges);
// "f" mentioned in the editorial
set_power_series f(1u << n);
for (vertex_set C = 1; C < 1u << n; ++C) {
// max C
const vertex t = library::bits::bit_length(C) - 1;
// {0, ..., t} - C
const vertex_set S = ((1u << (t + 1)) - 1) ^ C;
const size_t k = library::bits::popcount(S);
// "g_C" mentioned in the editorial
set_power_series g(1u << k);
for (vertex_set A = 0; A < 1u << k; ++A) {
// For more information about pdep, see https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_pdep&ig_expand=4939.
const vertex_set T = library::bits::pdep(A, S);
g[A] = f[T] * (E[T | C] - E[T] - E[C]);
}
// "h_C" mentioned in the editorial
const set_power_series h = library::set_power_series::subset_exp(g);
for (vertex_set A = 0; A < 1u << k; ++A) {
const vertex_set X = library::bits::pdep(A, S) | C;
f[X] += c[C] * h[A];
}
}
return library::set_power_series::subset_exp(f).back();
}
int main() {
size_t n, m;
std::cin >> n >> m;
std::vector<edge> edges(m);
for (auto &[u, v] : edges) {
std::cin >> u >> v;
--u, --v;
}
std::cout << solve(n, m, edges) << '\n';
}