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

题意

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

题解

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

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

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

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

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

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

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

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

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

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

[ks] fi,s×An|s|1|wkwks|fi+1,swk

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

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

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

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

这样写了一下 莫名奇妙就是 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; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9096357.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(870)  评论(5编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示