結果

問題 No.2507 Yet Another Subgraph Counting
コンテスト
ユーザー akakimidori
提出日時 2026-10-20 08:17:21
言語 C++23
(gcc 15.3.0 + boost 1.92.0 + ACL)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 18 ms / 2,000 ms
+ 544µs
コード長 11,987 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,268 ms
コンパイル使用メモリ 245,908 KB
実行使用メモリ 6,400 KB
最終ジャッジ日時 2026-10-20 08:17:32
合計ジャッジ時間 5,656 ms
ジャッジサーバーID
(参考情報)
judge4_0 / judge3_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 52
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("unroll-loops")

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <limits>
#include <queue>
#include <string>
#include <vector>
#include <numeric>
#include <span>
#include <ranges>

namespace mylib {

constexpr uint32_t calc_rem_expr(uint32_t m) {
    uint32_t t = 1;
    uint32_t s = ~m + 1;
    uint32_t n = ~(uint32_t)0 >> 2;
    while (n) {
        if (n & 1) t *= s;
        s *= s;
        n >>= 1;
    }
    return t;
}

template <uint32_t M>
class modint {
public:
    constexpr static uint32_t mod() { return M; }
    modint() : v_(0) {}
    template <class T>
    requires std::unsigned_integral<T>
    constexpr modint(T v) {
        v_ = raw(v % mod());
    }
    template <class T>
    requires std::signed_integral<T>
    constexpr modint(T v) {
        int64_t u = v % (int64_t)mod();
        if (u < 0) u += mod();
        v_ = raw(static_cast<uint32_t>(u));
    }
    constexpr uint32_t val() const {
        const uint32_t res = reduce(v_);
        return res >= mod() ? res - mod() : res;
    }
    constexpr modint &operator+=(const modint &rhs) {
        v_ += rhs.v_;
        v_ -= v_ >= 2 * mod() ? 2 * mod() : 0;
        return *this;
    }
    constexpr modint &operator-=(const modint &rhs) {
        v_ = v_ - rhs.v_ + (v_ < rhs.v_ ? 2 * mod() : 0);
        return *this;
    }
    constexpr modint &operator*=(const modint &rhs) {
        v_ = reduce((uint64_t)v_ * rhs.v_);
        return *this;
    }
    constexpr modint &operator/=(const modint &rhs) { return *this *= rhs.inv(); }
    template <class T>
    requires std::integral<T>
    constexpr modint pow(T n) const {
        assert(n >= 0);
        modint t = 1;
        modint r = *this;
        while (n) {
            if (n & 1) t *= r;
            r *= r;
            n >>= 1;
        }
        return t;
    }
    constexpr modint inv() const {
        assert(v_ > 0);
        return pow(mod() - 2);
    }
    friend constexpr modint operator+(const modint &lhs, const modint &rhs) {
        return modint(lhs) += rhs;
    }
    friend constexpr modint operator-(const modint &lhs, const modint &rhs) {
        return modint(lhs) -= rhs;
    }
    friend constexpr modint operator*(const modint &lhs, const modint &rhs) {
        return modint(lhs) *= rhs;
    }
    friend constexpr modint operator/(const modint &lhs, const modint &rhs) {
        return modint(lhs) /= rhs;
    }
    constexpr modint operator+() const {
        return *this;
    }
    constexpr modint operator-() const {
        modint res;
        res.v_ = v_ == 0 ? 0 : 2 * mod() - v_;
        return res;
    }
    constexpr bool operator==(const modint &rhs) const {
        return (v_ >= mod() ? v_ - mod() : v_) == (rhs.v_ >= mod() ? rhs.v_ - mod() : rhs.v_);
    }
private:
    uint32_t v_;
    constexpr static uint32_t rem = calc_rem_expr(M);
    constexpr static uint64_t ini = ((((uint64_t)1 << 32) % mod()) << 32) % mod();
    constexpr static uint32_t reduce(const uint64_t x) {
        return (x + (uint32_t)x * rem * (uint64_t)mod()) >> 32;
    }
    constexpr static uint32_t raw(uint32_t v) { return reduce(v * ini); }
    static_assert(mod() < (1 << 30));
    static_assert((mod() & 1) == 1);
};

template <typename T>
class Binomial {
public:
    Binomial(const int n) : n_(n) {
        assert(n >= 0);
        fact_.resize(n + 1, T(1));
        ifact_.resize(n + 1, T(1));
        inv_.resize(n + 1, T(1));
        for (const int i : std::views::iota(1, n + 1)) fact_[i] = i * fact_[i - 1];
        ifact_[n] = 1 / fact_[n];
        for (const int i : std::views::iota(0, n) | std::views::reverse) {
            ifact_[i] = ifact_[i + 1] * (i + 1);
            inv_[i + 1] = ifact_[i + 1] * fact_[i];
        }
    }
    T fact(int n) {
        assert(n <= n_);
        return fact_[n];
    }
    T ifact(int n) {
        assert(n <= n_);
        return ifact_[n];
    }
    T binom(int n, int k) {
        if (k < 0 || n < k) return 0;
        assert(n <= n_);
        return fact(n) * ifact(n - k) * ifact(k);
    }
    T inv(int n) {
        assert(0 < n && n <= n_);
        return inv_[n];
    }
private:
    const int n_;
    std::vector<T> fact_, ifact_, inv_;
};

}    // namespace mylib

namespace sps {

template <typename T>
std::vector<T> conv(std::span<const T> a, std::span<const T> b) {
    std::size_t len = a.size(), n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n) && len == b.size());
    std::vector<T> x((n + 1) << n, T(0)), y((n + 1) << n, T(0));
    for (std::size_t i : std::views::iota(0ul, len)) {
        const std::size_t cnt = std::popcount(i);
        x[i * (n + 1) + cnt] = a[i];
        y[i * (n + 1) + cnt] = b[i];
    }
    for (std::size_t i : std::views::iota(0ul, len)) {
        std::size_t s = (n + 1) * i, t = (n + 1) * (i + 1), z = std::countr_zero(i | (1 << n));
        for (std::size_t j : std::views::iota(0ul, z) | std::views::reverse) {
            std::size_t w = (n + 1) << j;
            for (std::size_t k : std::views::iota(0ul, w)) {
                x[s + w + k] += x[s + k];
                y[s + w + k] += y[s + k];
            }
        }
        std::size_t cnt = std::popcount(i);
        for (std::size_t j : std::views::iota(cnt, std::min(n, 2 * cnt) + 1) | std::views::reverse) {
            T v = 0;
            for (std::size_t k : std::views::iota(j - cnt, cnt + 1)) v += x[s + k] * y[s + j - k];
            x[s + j] = v;
        }
        std::size_t o = std::countr_one(i);
        for (std::size_t j : std::views::iota(0ul, o)) {
            std::size_t w = (n + 1) << j, s = t - 2 * w;
            for (std::size_t k : std::views::iota(0ul, w)) x[s + w + k] -= x[s + k];
        }
    }
    std::vector<T> c(1 << n);
    for (std::size_t i : std::views::iota(0ul, len)) c[i] = x[i * (n + 1) + std::popcount(i)];
    return c;
}

// a * b = c なるbを返す a[0] = 1 要求
template <typename T>
std::vector<T> conv_inv(std::span<const T> a, std::span<const T> c) {
    std::size_t len = a.size(), n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n) && len == c.size() && a[0] == 1);
    std::vector<T> x((n + 1) << n, T(0)), y((n + 1) << n, T(0));
    for (std::size_t i : std::views::iota(0ul, len)) {
        std::size_t cnt = std::popcount(i);
        x[i * (n + 1) + cnt] = a[i];
        y[i * (n + 1) + cnt] = c[i];
    }
    for (std::size_t i : std::views::iota(0ul, len)) {
        std::size_t s = (n + 1) * i, t = (n + 1) * (i + 1), z = std::countr_zero(i | (1 << n));
        for (std::size_t j : std::views::iota(0ul, z) | std::views::reverse) {
            std::size_t w = (n + 1) << j;
            for (std::size_t k : std::views::iota(0ul, w)) {
                x[s + w + k] += x[s + k];
                y[s + w + k] += y[s + k];
            }
        }
        const std::size_t cnt = std::popcount(i);
        for (std::size_t j : std::views::iota(0ul, n + 1)) {
            const T v = y[s + j];
            for (std::size_t k : std::views::iota(1ul, std::min(n - j, cnt) + 1)) y[s + j + k] -= v * x[s + k];
        }
        std::size_t o = std::countr_one(i);
        for (std::size_t j : std::views::iota(0ul, o)) {
            std::size_t w = (n + 1) << j, s = t - 2 * w;
            for (std::size_t k : std::views::iota(0ul, w)) y[s + w + k] -= y[s + k];
        }
    }
    std::vector<T> b(1 << n);
    for (std::size_t i : std::views::iota(0ul, len)) b[i] = y[i * (n + 1) + std::popcount(i)];
    return b;
}

template <class T>
std::vector<T> exp(std::span<const T> a) {
    std::size_t len = a.size(), n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n) && a[0] == T(0));
    std::vector<T> res(len, T(1));
    for (std::size_t i : std::views::iota(0ul, n)) {
        const auto c = conv<T>(std::span{res}.subspan(0, 1 << i), a.subspan(1 << i, 1 << i));
        std::ranges::copy(c, res.begin() + (1 << i));
    }
    return res;
}

template <class T>
std::vector<T> log(std::span<const T> a) {
    std::size_t len = a.size(), n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n) && a[0] == T(1));
    std::vector<T> res(len, T(0));
    for (std::size_t i : std::views::iota(0ul, n)) {
        const auto c = conv_inv<T>(a.subspan(0, 1 << i), a.subspan(1 << i, 1 << i));
        std::ranges::copy(c, res.begin() + (1 << i));
    }
    return res;
}

// f: 多項式
// g: sps
template <typename T>
std::vector<T> composite(std::span<const T> f, std::span<const T> g) {
    std::size_t len = g.size(), n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n));
    std::vector<T> t(f.begin(), f.end());
    std::vector<std::vector<T>> dp(n + 1, {T(0)});
    for (auto &d : dp) {
        if (t.empty()) break;
        for (const auto &v : t | std::views::reverse) d[0] = d[0] * g[0] + v;
        for (std::size_t i : std::views::iota(1ul, t.size())) t[i - 1] = T(i) * t[i];
        t.pop_back();
    }
    for (std::size_t i : std::views::iota(0ul, n)) {
        for (std::size_t j : std::views::iota(1ul, dp.size())) {
            const auto h = conv<T>(g.subspan(1 << i, 1 << i), dp[j]);
            std::ranges::copy(h, std::back_inserter(dp[j - 1]));
        }
        dp.pop_back();
    }
    return dp[0];
}

}    // namespace sps

template <class T, class F>
void bitwise_transform(std::vector<T> &a, F f) {
    const std::size_t len = a.size();
    const int n = std::countr_zero(len);
    assert(len > 0 && len == (1ul << n));
    for (int i = 0; i < n; ++i) {
        const int w = 1 << i;
        for (int j = 0; j < (1 << n); j += 2 * w) {
            for (int k = 0; k < w; ++k) {
                f(a[j + k], a[j + k + w]);
            }
        }
    }
}

using mint = mylib::modint<998244353>;

using i64 = int64_t;
using i32 = int32_t;
using u64 = uint64_t;
using u32 = uint32_t;

template <typename T>
T read() {
    T v;
    std::cin >> v;
    return v;
}

template <class T>
std::vector<T> vec(T elem, int len) {
    return std::vector<T>(len, elem);
}

void yuki2507() {
    const auto n = read<i32>();
    const auto m = read<i32>();
    std::vector<int> g(n, 0);
    for (i32 _ : std::views::iota(0, m)) {
        const auto a = read<i32>() - 1;
        const auto b = read<i32>() - 1;
        g[a] |= 1 << b;
        g[b] |= 1 << a;
    }
    std::vector<u64> dp(1 << n, 0);
    for (const i32 r : std::views::iota(0, n)) {
        auto ep = vec(vec(u64(0), 1 << r), r);
        dp[1 << r] = 2;
        for (const i32 v : std::views::iota(0, r)) if ((g[r] >> v) & 1) ep[v][1 << v] = 1;
        for (const i32 bit : std::views::iota(1, 1 << r)) {
            for (const i32 v : std::views::iota(0, r)) {
                const auto w = ep[v][bit];
                if (w == 0) continue;
                const auto mask = ~bit & g[v];
                for (const i32 u : std::views::iota(0, r)) if ((mask >> u) & 1) ep[u][bit | (1 << u)] += w;
                if (std::popcount((u32)bit) > 1 && ((g[r] >> v) & 1)) {
                    dp[bit | (1 << r)] += w;
                }
            }
        }
    }
    for (auto &v : dp) v /= 2;
    for (const i32 r : std::views::iota(1, n) | std::views::reverse) {
        std::vector<std::vector<u64>> p(2);
        for (const auto [i, v] : dp | std::views::enumerate) {
            p[(i >> r) & 1].emplace_back(v);
        }
        const i32 mask = (1 << r) - 1;
        for (auto [i, p] : p[0] | std::views::enumerate) {
            p *= std::popcount(u32(mask & g[r] & i));
        }
        const auto res = sps::conv<u64>(p[1], sps::exp<u64>(p[0]));
        auto a = dp | std::views::chunk(2 << r);
        auto b = res | std::views::chunk(1 << r);
        for (auto [x, y] : std::views::zip(a, b)) {
            std::ranges::copy(y, x.begin() + (1 << r));
        }
    }
    std::cout << sps::exp<u64>(dp)[(1 << n) - 1] << std::endl;
}

int main(void) {
    std::cin.tie(nullptr);
    std::ios::sync_with_stdio(false);
    yuki2507();
    return 0;
}
0