LOJ #2540. 「PKUWC 2018」随机算法(概率dp)

题意

LOJ #2540. 「PKUWC 2018」随机算法

题解

朴素的就是 (O(n3^n)) dp 写了一下有 (50pts) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 , 考虑了并且在独立集中 , 还没考虑 . 转移就很显然了 qwq

然后要优化嘛 , 把其中两个状态合起来 , 也就是分成考虑了和没考虑了的两种 .

其中考虑了的那种 , 只会存在两种状态 , 要么是在独立集内 , 要么就是与独立集联通 , 没有考虑的 绝对不和独立集联通 就行了 .

然后我们枚举一个集合 , 考虑强制把一个点选进来 . 如果要选它 , 那么它周围的一圈都不能去选 .

为了使这个 dp 不存在后效性 , 我们不能让之后的选的点连得点存在于独立集中 , 我们把他周围一圈的都放进来就行了 .

也就是说当前维护的集合 , 会被最外面没有存在于独立集中的一圈给包围住 .

然后连上来的时候会有很多种排列的方式 , 直接乘上一个排列数就行了 . (相当于预留位置)

最后算答案因为是概率 , 除以 (frac{1}{n!}) 就行了 .

这些我都是问 DOFY 才懂的 , 还是太菜啦 ~

具体来说 方程是这样的 ((displaystyle f_{i,s}) 当前选了 (i) 个点 , 考虑过的集合是 (s) , 与 (k) 相邻的所有点(包括它自己)的集合为 (w_k) ):

[[k otin s] ~ displaystyle f_{i,s} imes A_{n-|s|-1}^{|w_k-w_k cap s|} o f_{i+1,s cup w_k} ]

时间复杂度是 (O(n^2 2^n)) 可以通过此题了 .

然后进行一波优化 , 对于一个确定的考虑过的集合 (s) 那么它选取最大独立集 , 是选取全局的最大独立集的必要条件 .

(这个应该显然吧 ... 因为最外圈你不要的话 , 如果里面不是最大独立集的话 , 外面取得最大也达不到全局最大)

那么第一位考虑选点的就可以不要了 , 时间复杂度就是 (O(n2^n)) .

这样写了一下 莫名奇妙就是 LOJ 最快的了 ?

代码

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
	freopen ("2540.in", "r", stdin);
	freopen ("2540.out", "w", stdout);
#endif
}

const int N = 20;
int n, m, Con[N];

typedef long long ll;
const int Mod = 998244353;

ll fpm(ll x, int power) {
	ll inv = 1;
	for (; power; power >>= 1, (x *= x) %= Mod)
		if (power & 1) (inv *= x) %= Mod;
	return inv;
}

ll fac[N + 5], ifac[N + 5];
void Init(int maxn) {
	fac[0] = ifac[0] = 1;
	For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod;
	ifac[maxn] = fpm(fac[maxn], Mod - 2);
	Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
}

int dp[1 << N], bit[1 << N], MaxSize[1 << N];;

inline int A(int n, int m) { if (m > n || n < 0 || m < 0) return 0; return fac[n] * ifac[n - m] % Mod; }

int main () {
	File();

	n = read(); m = read(); Init(n); 
	For (i, 1, m) {
		int u = read() - 1, v = read() - 1;
		Con[u] |= (1 << v);
		Con[v] |= (1 << u);
	}

	For (i, 0, n - 1) Con[i] |= (1 << i);

	dp[0] = 1;
	int maxsta = (1 << n) - 1;
	For (i, 0, maxsta) bit[i] = bit[i >> 1] + (i & 1);

	For (i, 0, maxsta) if (dp[i]) {
		For (j, 0, n - 1) if (!((1 << j) & i)) {
			register int Next = i | Con[j];
			if (chkmax(MaxSize[Next], MaxSize[i] + 1)) dp[Next] = 0;
			if (MaxSize[Next] == MaxSize[i] + 1) (dp[Next] += 1ll * dp[i] * A(n - bit[i] - 1, bit[Con[j] ^ (Con[j] & i)] - 1) % Mod) %= Mod;
		}
	}

	printf ("%lld
", 1ll * dp[maxsta] * ifac[n] % Mod);
    return 0;
}
原文地址:https://www.cnblogs.com/zjp-shadow/p/9096357.html