如果只有 $ 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;
}