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 \notin s] ~ \displaystyle f_{i,s} \times A_{n-|s|-1}^{|w_k-w_k \cap s|} \to 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\n", 1ll * dp[maxsta] * ifac[n] % Mod);
return 0;
}
上一篇:Android:关于Edittext的一些设置


下一篇:用Jekyll在github上写博客——《搭建一个免费的,无限流量的Blog》的注脚