loj2540. 「PKUWC2018」随机算法

题意

略。

题解

听说考场上暴力搜出独立集有90分
这道题的状态还是挺难找的。
初始排列为空。考虑设\(f_{s, i}\)表示当前状态,独立集为\(s\),已经不在独立集里面(即与\(s\)中的点有连边)且还没有加入排列的点数为\(i\)
则有初始状态\(f_{0, 0} = 1\)
考虑转移,如果某一个点可以加入这个独立集,则:

\[f_{s \cup \{x\}, i + \text{new}(x, s)} += f_{s, i} \ (x \notin s) \]

其中\(new(x, s)\)代表的是\(x\)有连边,并且不属于\(s\)且不与\(s\)中任何点有连边的点的个数。
这个操作代表将\(x\)加入排列。
只有这样一种操作是不够的,考虑要把已经不在独立集里面且还没有加入排列的点加入排列,如果有\(i\)个这样的点,那么这次可以选择任何一个加入。

\[f_{s, i - 1} += f_{s, i} \]

考虑到转移一定构成了一个DAG(先按集合\(s\)的偏序,再按点数\(i\)的偏序),所以是没问题的。
但是转移的时候要注意后一种操作是可以不断地做的,所以\(i\)的枚举方向是从\(n\)\(1\)
复杂度\(\mathcal O(2 ^ n n ^ 2)\)

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;

const int N = 20, M = 1 << 20, mod = 998244353;
int n, m, c, ans, e[M], d[N][M], t[M], f[N][M];
int power (int x, int y) {
	int ret = 1;
	for ( ; y; y >>= 1, x = 1ll * x * x % mod) {
		if (y & 1) {
			ret = 1ll * ret * x % mod;
		}
	}
	return ret;
}
void U (int &x, int y) {
	if ((x += y) >= mod) {
		x -= mod;
	}
}
int main () {
	cin >> n >> m;
	for (int i = 0; i < n; ++i) {
		e[1 << i] = 1 << i;
	}
	for (int i = 1, x, y; i <= m; ++i) {
		cin >> x >> y, --x, --y;
		e[1 << x] |= 1 << y, e[1 << y] |= 1 << x;
	}
	m = 1 << n;
	for (int i = 0; i < n; ++i) {
		for (int s = 0; s < m; ++s) {
			if (s >> i & 1) {
				e[s] |= e[s ^ 1 << i];
			}
		}
	}
	for (int i = 0; i < n; ++i) {
		for (int s = 0; s < m; ++s) {
			if (~e[s] >> i & 1) {
				d[i][s] = __builtin_popcount(e[s] | e[1 << i] ^ (1 << i)) - __builtin_popcount(e[s]);
			}
		}
	}
	f[0][0] = 1;
	for (int s = 0; s < m; ++s) {
		for (int i = n; i; --i) {
			U(f[i - 1][s], 1ll * f[i][s] * i % mod);
		}
		for (int i = n; ~i; --i) {
			if (f[i][s]) {
				for (int x = 0; x < n; ++x) {
					if (~e[s] >> x & 1) {
						U(f[i + d[x][s]][s | 1 << x], f[i][s]);
					}
				}
			}
		}
	}
	for (int s = 0; s < m; ++s) {
		if (f[0][s]) {
			if (__builtin_popcount(s) > c) {
				c = __builtin_popcount(s), ans = 0;
			}
			if (__builtin_popcount(s) == c) {
				U(ans, f[0][s]);
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
		ans = 1ll * ans * power(i, mod - 2) % mod;
	}
	cout << ans << endl;
	return 0;
}
posted @ 2019-12-09 09:46  psimonw  阅读(254)  评论(0编辑  收藏  举报