洛谷 P5492 [PKUWC2018]随机算法 题解

一、题目:

洛谷原题

二、思路:

这道题上来就有一个比较显然的转化,但只可惜我脑子太笨,没转化过来。考虑随机排列来判断是否能构成最大独立集的概率,就等于每次随机一个点,是否能构成当前点集中最大独立集的概率。

所以设状态为 \(F(S)\),表示构成 \(S\) 中的最大独立集的概率。

先预处理出 \(S\) 中最大独立集的大小 \(siz(S)\):枚举最后加入 \(S\) 中的点 \(x\),将与 \(x\) 相连的所有点以及 \(x\) 本身删去后,得到点集 \(T\)。则

\[siz(S)=\max_T\{siz(T)+1\} \]

于是 \(F\) 的状态转移方程为

\[F(S)=\dfrac{1}{|S|}\sum_{siz(S)=siz(T)+1} F(T) \]

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <bitset>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 22, maxm = maxn * maxn, mod = 998244353;

int n, m, head[maxn], tot, cnt[1 << maxn], siz[1 << maxn];
int c[maxn];

long long f[1 << maxn];

struct Edge {
    int y, next;
    Edge() {}
    Edge(int _y, int _next) : y(_y), next(_next) {}
}e[maxm];

vector<int>vec[maxn];

inline void connect(int x, int y) {
    e[++ tot] = Edge(y, head[x]);
    head[x] = tot;
}

inline int count(int s) {
    int res = 0;
    while (s) {
        res += (s & 1);
        s >>= 1;
    }
    return res;
}

inline int call(int x, int i) { return (x >> i) & 1; }

inline long long power(long long a, long long b) {
    long long res = 1;
    for (; b; b >>= 1) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}

inline long long div(int x) { return power(x, mod - 2); }

inline void init(void) {
    for (int s = 0; s < (1 << n); ++ s) {
        cnt[s] = count(s);
        vec[cnt[s]].push_back(s);
    }
    for (int x = 0; x < n; ++ x) {
        c[x] = (1 << x);
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].y;
            c[x] += (1 << y);
        }
    }
}

int main() {
    n = read(); m = read();
    for (int i = 1; i <= m; ++ i) {
        int x = read(), y = read(); -- x; -- y;
        connect(x, y); connect(y, x);
    }
    init();
    
    f[0] = 1;
    siz[0] = 0;
    for (int num = 1; num <= n; ++ num) {
        for (auto s: vec[num]) {
            for (int x = 0; x < n; ++ x) {
                if (!call(s, x)) continue;
                int t = (~c[x]) & s;
                siz[s] = max(siz[s], siz[t] + 1);
            }

            for (int x = 0; x < n; ++ x) {
                if (!call(s, x)) continue;
                int t = (~c[x]) & s;
                if (siz[t] + 1 == siz[s]) { (f[s] += f[t]) %= mod; }
            }
            (f[s] *= div(cnt[s])) %= mod;

        }
    }
    printf("%lld\n", f[(1 << n) - 1]);
    return 0;
}
posted @ 2021-05-11 10:40  蓝田日暖玉生烟  阅读(62)  评论(0编辑  收藏  举报