[ARC105F] Lights Out on Connected Graph 题解

发布时间 2023-11-07 20:41:11作者: User-Unauthorized

题意

给定一个 \(N\) 个点 \(M\) 条边的简单无向联通图 \(G\)。每个边有红和蓝两种颜色,初始时每条边均是红色。

现在通过移除 \(G\) 中的一些边来获得一个新的无向图 \(G^{\prime}\),求在所有的 \(2^M\) 种方案中有多少种方案可以使得 \(G^{\prime}\) 满足如下条件:

  • \(G^{\prime}\) 是联通图

  • 通过采取如下操作可以使得所有的边变为蓝色:

    • 选择一个点 \(u\),将与 \(u\) 相邻的所有边变为蓝色

\(998244353\) 取模。

  • \(1 \le N \le 17\)
  • \(N - 1 \le M \le \frac{N\left(N - 1\right)}{2}\)

题解

首先我们可以发现,符合要求的子图 \(G^\prime\) 一定是二分图。

发现联通的要求很难满足,故考虑先计算不考虑联通限制下的答案。

\(g(S)\) 表示仅考虑与点集 \(S\) 相关的边,求使得图为二分图但不要求联通的方案数。

考虑对图进行黑白染色,通过枚举白色点集合 \(T\) 可以完成计算:

\[g(S) = \sum\limits_{T \subseteq S} 2^{\operatorname{count}_{S} - \operatorname{count}_{T} - \operatorname{count}_{S \setminus T}} \]

其中 \(\operatorname{count}_{S}\) 表示与点集 \(S\) 相关的边的数量。可以发现上式中的指数项即连接黑色点和白色点的边的数量。

\(\operatorname{count}_{S}\) 的值可以在通过对每个点集枚举所有边来实现,这里不多赘述。

下面考虑如何计算联通的方案数。

\(f(S)\) 表示仅考虑与点集 \(S\) 相关的边,求使得图为二分图且要求联通的方案数。

可以发现 \(f(S)\) 中包含的方案实际上是 \(g(S)\) 中去除了不联通的方案,因此可以考虑枚举出所有不联通的方案。

若不联通,那么图一定具有至少两个联通块,那么我们可以枚举 \(S\) 中编号最小的点所在的联通块,然后枚举该联通块中的点集 \(T\),那么 \(S \setminus T\) 的连通性是没有限制。设 \(u = \min\limits_{x \in S} x\),即点集 \(S\) 中的最小元素,那么有转移

\[f(S) = g(S) - \sum\limits_{T \subset S \land u \in T} f(T) \times g(S \setminus T) \]

时间复杂度 \(O(3^N + 2^NM)\),空间复杂度 \(O(2^N + M)\),可以通过。

Code

#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;

namespace MODINT {
    constexpr valueType MOD = 998244353;

    template<typename T1, typename T2, typename T3 = valueType>
    void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a + b;

        if (a >= mod)
            a -= mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a - b;

        if (a < 0)
            a += mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
        return a + b >= mod ? a + b - mod : a + b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
        return a - b < 0 ? a - b + mod : a - b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
        return (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
        a = (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a, mod);

            Mul(a, a, mod);
            b = b >> 1;
        }

        return result;
    }

    constexpr valueType Inv2 = (MOD + 1) / 2;
}// namespace MODINT

using namespace MODINT;

bool check(valueType x, valueType k) {
    return x & (1 << (k - 1));
}

valueType lowBit(valueType n) {
    return n & -n;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, M;

    std::cin >> N >> M;

    PairVector edges(M);

    for (auto &iter : edges)
        std::cin >> iter.first >> iter.second;

    valueType const S = 1 << N;

    ValueVector count(S, 0);

    for (valueType s = 0; s < S; ++s) {
        for (valueType i = 0; i < M; ++i) {
            auto [u, v] = edges[i];

            if (check(s, u) && check(s, v))
                Inc(count[s], 1);
        }
    }

    ValueVector G(S, 0), F(S, 0);

    for (valueType s = 0; s < S; ++s) {
        G[s] = 1;

        for (valueType t = s; t > 0; t = (t - 1) & s)
            Inc(G[s], pow(2, count[s] - count[t] - count[s ^ t]));
    }

    for (valueType s = 0; s < S; ++s) {
        F[s] = G[s];

        for (valueType t = s; t > 0; t = (t - 1) & s)
            if (lowBit(t) < lowBit(s ^ t))
                Dec(F[s], mul(F[t], G[s ^ t]));
    }

    std::cout << mul(F[S - 1], Inv2) << std::endl;

    return 0;
}