【THUWC 2017】随机二分图

如果只有 $ t=0 $ 的边,可以直接把边关于左边的点排序, $ dp[S] $ 表示左边匹配了前 $ |S| $ 个点,右边匹配集合为 $ S $ 的方案数,这样是40分。

正解是考虑把边拆分成独立的若干边。

根据期望的线性性,我们只需要对于每一种匹配方案,求出其能成功的概率。如果我们能够建一张新图,使得对于任意匹配方案,其成功的概率不变,并且所有边相互独立,就可以用40分的算法进行DP.

当 $ t = 1 $ 时,考虑如果直接把两条边当做相互独立的。如果在一个匹配中,用到了其中的一条边,能满足的概率是 $ \frac{1}{2} $ , 没有问题;但是如果一个匹配中用到了其中的两条边,我们算出来它满足的概率是 $ \frac{1}{4} $ ,但实际上概率应该是 $ \frac{1}{2} $ ,所以我们补一条连接这四个点,出现概率为 $ \frac{1}{4} $ 的边,这三条相互独立的边就等价于原来的那组边。

$ t = 2 $ 时也同理,只是加上去的那条边概率应该是 $ -\frac{1}{4} $

这样一来 $ DP $ 的状态就不一定能满足左边匹配的一定是前 $ |S| $ 个点,但是我们转移的时候可以搞一个记忆化搜索,还是以左边的点为标准将边排序,实测跑得很快。

#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LL long long
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int inv2 = (mod + 1) / 2;
const int inv4 = (mod + 1) / 4;

template<typename T> void read(T &x) {
   char c = getchar(); int f = 0;
   while (c < '0' || c > '9') f |= (c == '0'), c = getchar();
   for (x = 0; c >= '0' && c <= '9'; c = getchar())
      x = (x << 3) + (x << 1) + (c ^ '0');
   if (f) x = -x;
}

int n, m, E;
pii e[N];
map<int, int> dp;

void upd(int &x, int y) {
   (x += y) >= mod ? x -= mod : 0;
}

int Qpow(int x, int p) {
   int ans = 1;
   while (p) {
      if (p & 1) ans = 1LL * ans * x % mod;
      x = 1LL * x * x % mod;
      p >>= 1;
   }
   return ans;
}

int dfs(int x) {
   if (dp.count(x)) return dp[x];
   int ans = 0, low = 0;
   for (int i = n - 1; i >= 0; --i) {
      if (~x >> i & 1) low = i;
   }
   for (int i = 1; i <= E; ++i) {
      if ((~x & e[i].fi) == e[i].fi && (e[i].fi >> low & 1)) {
         upd(ans, 1LL * dfs(x ^ e[i].fi) * e[i].se % mod);
      }
   }
   return dp[x] = ans;
}

int main() {
   read(n); read(m);
   for (int i = 1; i <= m; ++i) {
      int op, a1, b1, a2, b2;
      read(op);
      read(a1); read(b1); b1 += n;
      --a1; --b1;
      if (op == 0) {
         e[++E] = mp((1 << a1) + (1 << b1), inv2);
      }
      else {
         read(a2); read(b2); b2 += n;
         --a2; --b2;
         e[++E] = mp((1 << a1) + (1 << b1), inv2);
         e[++E] = mp((1 << a2) + (1 << b2), inv2);
         if (a1 == a2 || b1 == b2) continue;
         if (op == 1) {
            ++E;
            e[E] = mp(e[E - 1].fi + e[E - 2].fi, inv4);
         }
         if (op == 2) {
            ++E;
            e[E] = mp(e[E - 1].fi + e[E - 2].fi, -inv4 + mod);
         }
      }
   }
   dp[(1 << (n * 2)) - 1] = 1;
   cout << 1LL * dfs(0) * Qpow(2, n) % mod << endl;
   // for (int i = 0; i < (1 << (n * 2)); ++i)
   //    cout << i << ' ' << dp[i] << endl;
   return 0;
}
posted @ 2019-10-23 20:45  Vexoben  阅读(130)  评论(0编辑  收藏  举报