WC模拟(12.20) T2 随机二分图(bzoj5006)

随机二分图

题目背景:

12.20 WC模拟T2 (bzoj5006)

分析:记忆化搜索 + 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;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值