[状压DP][概率与期望][二分图] BZOJ 5006 && LOJ #2290. 「THUWC 2017」随机二分图

本文介绍了一种结合状态压缩动态规划与记忆化搜索解决概率性匹配问题的方法。通过将特殊类型的边转换为基本类型,利用状压DP降低复杂度,并采用记忆化搜索避免重复计算,最终求得完备匹配数量的期望。

Solution

好神的做法。
如果只有type=0的话,直接状压DP就好了。
fS,T表示左边集合为S,右边匹配的集合为T的完备匹配的数量的期望。
而后面两种可以这么考虑把一组边拆掉。
type=1的话拆成两条50%可能出现的边和一组25%同时出现的边(若重点是不可能同时选的)。
这样的话同时出现的概率为50%×50%+25%=50%,两条边同时不出现的概率为(125%)(50%×50%),一条边出现另一条边不出现的概率就为0。这样的连边方式就与原先等价了。
type=2的情况同上考虑,拆成两条50%可能出现的边和一组25%同时出现的边。
显然暴力这么打复杂度过不去的。但理性分析一下给记忆化搜索的过程中的状态定个序,用map存一下就好了。
跑的好慢啊

#include <bits/stdc++.h>
using namespace std;

const int N = 16;
const int MOD = 1000000007;
const int INV2 = MOD + 1 >> 1;
const int INV4 = MOD + 1 >> 2;
typedef pair<int, int> Pairs;
typedef long long ll;

inline char get(void) {
  static char buf[100000], *S = buf, *T = buf;
  if (S == T) {
    T = (S = buf) + fread(buf, 1, 100000, stdin);
    if (S == T) return EOF;
  }
  return *S++;
}
template<typename T>
inline void read(T &x) {
  static char c; x = 0; int sgn = 0;
  for (c = get(); c < '0' || c > '9'; c = get()) if (c = '-') sgn = 1;
  for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
  if (sgn) x = -x;
}

map<int, int> f[1 << N];
int n, m, Gcnt, t, x, y;
struct edge {
  int S, p, c;
  edge(int _S = 0, int _p = 0, int _c = 0): S(_S), p(_p), c(_c) {}
};
edge G[N * N << 2];

inline void Add(int &x, int a) {
  x = (x + a) % MOD;
}
inline int F(int S) {
  if (!S) return 1;
  int T0 = S >> n, S0 = S ^ (T0 << n);
  if (f[S0].count(T0)) return f[S0][T0];
  int &g = f[S0][T0];
  for (int i = 1; i <= Gcnt; i++) {
    int T = G[i].S;
    if ((T & S) == T && S < (T << 1))
      Add(g, (ll)F(S ^ T) * G[i].p % MOD);
  }
  return g;
}

int main(void) {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
  read(n); read(m);
  for (int i = 1; i <= m; i++) {
    read(t); read(x); read(y);
    int S1 = (1 << (x - 1)) | (1 << (y + n - 1));
    G[++Gcnt] = edge(S1, INV2, 1);
    if (t) {
      read(x); read(y);
      int S2 = (1 << (x - 1)) | (1 << (y + n - 1));
      G[++Gcnt] = edge(S2, INV2, 1);
      if (S1 & S2) continue;
      if (t == 1) G[++Gcnt] = edge(S1 | S2, INV4, 2);
      else G[++Gcnt] = edge(S1 | S2, MOD - INV4, 2);
    }
  }
  cout << (1ll << n) * F((1 << (n * 2)) - 1) % MOD << endl;
  return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值