随机二分图
题目背景:
分析:记忆化搜索 + hash状态剪枝
据说,这个题的20分和100分是一个复杂度······然后自己写写发现是真的·····考虑,怎么做这道题,首先,对于第一种边,直接转移就好了,对于第二种边,我们可以把它看做是每一条50%的概率出现,再加上25%的概率同时出现,对于第三种边,看做是每一条50%的概率出现,再减去25%的概率同时出现。也就是说在全部都为第一种边的基础上,加上或者减去某些可能,因为要乘上2n,相当于每加一条边就乘上2,但是按道理应该是所以说从所有的50%概率转移一条边,* 2就相当于直接加上一份贡献,25%转移两条边,就相当于* 4,也是直接加上一份贡献,所以直接状压f[i][j]表示,当前左边被选择的点的状态为i,右边被选择的点的状态为j,当前的方案数是多少,然后向上述所说转移即可。注意枚举转移时,只用枚举最小的点,否则会算重复,需要用哈希优化状态数,否则存不下。
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
///*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
if (c == '-') iosig = true;
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int hash_mod = 10000000 + 19;
const int mod = 1000000000 + 7;
const int MAXN = 20;
int type[MAXN][MAXN], next[MAXN][MAXN];
int n, m, t, x, y, a, b;
struct node {
int a, b, f;
node() {}
node(int a = 0, int b = 0, int f = 0) : a(a), b(b), f(f) {}
} ;
std::vector<node> hash[hash_mod];
inline int hash_val(int a, int b) {
return (a << n | b) % hash_mod;
}
inline void add(int &x, int t) {
x += t, (x >= mod) ? (x -= mod) : (0);
}
inline void read_in() {
memset(type, -1, sizeof(type));
R(n), R(m);
for (int i = 1; i <= m; ++i) {
R(t);
if (t == 0) R(x), R(y), x--, y--, type[x][y] = 0;
else {
R(x), R(y), R(a), R(b), x--, y--, a--, b--;
type[x][y] = type[a][b] = t;
next[x][y] = a * MAXN + b, next[a][b] = x * MAXN + y;
}
}
hash[0].push_back(node(0, 0, 1));
}
inline int dfs(int a, int b) {
int mask = hash_val(a, b);
for (int p = 0; p < hash[mask].size(); ++p) {
node *cur = &hash[mask][p];
if (cur->a == a && cur->b == b) return cur->f;
}
int ret = 0;
for (int i = 0; i < n; ++i)
if ((1 << i) & a) {
for (int j = 0; j < n; ++j)
if ((1 << j) & b) {
switch (type[i][j]) {
case 0: add(ret, dfs(a ^ (1 << i), b ^ (1 << j)));
break ;
case 1: {
int sta = a ^ (1 << i), stb = b ^ (1 << j);
add(ret, dfs(sta, stb));
int u = next[i][j] / MAXN, v = next[i][j] % MAXN;
if (u != i && v != j && (a & (1 << u))
&& (b & (1 << v)))
add(ret, dfs(sta ^ (1 << u),
stb ^ (1 << v)));
break ;
}
ca se 2: {
int sta = a ^ (1 << i), stb = b ^ (1 << j);
add(ret, dfs(sta, stb));
int u = next[i][j] / MAXN, v = next[i][j] % MAXN;
if (u != i && v != j && (a & (1 << u))
&& (b & (1 << v)))
add(ret, mod - dfs(sta ^ (1 << u),
stb ^ (1 << v)));
break ;
}
}
}
return hash[mask].push_back(node(a, b, ret)), ret;
}
}
int main() {
read_in();
printf("%d", dfs((1 << n) - 1, (1 << n) - 1));
return 0;
}