洛谷 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;
}