@description@

一个左右各 n 个点的二分图,图中的边会按照一定的规律随机出现。将这些边分到若干个组中(每条边至多属于一个组):

第(1)类组每组有一条边,该边有 50% 的概率出现。

第(2)类组每组有两条边,这两条边有 50% 的概率同时出现,有 50% 的概率同时不出现。

第(3)类组每组有两条边,这两条边恰好出现一条,各有 50% 的概率出现。

问完美匹配数量的期望。

input

第一行两个数 n 和 m,表示图左右点数的数量和边的组的个数。我们用 (a,b) (其中 1≤a, b≤n )表示一条左端点为二分图左侧第 a 个点,右端点为二分图右侧第 b 个点的边。

接下来 m 行,每行描述一个组。开头第一个数 t 表示组的种类,t = 0 表示是第(1)类组,t = 1 表示是第(2)类组,t = 2 表示是第(3)类组。如果 t=0, 接下来两个数 a1, b1 表示组内的边 (a1, b1);否则,接下来四个数 a1, b1, a2, b2 表示该组内的两条边分别为 (a1, b1) 和 (a2, b2)。保证每条边至多出现一次。

output

假设期望的完美匹配数量是 E。输出 \((2^{n} E) \bmod (10^9 + 7)\)

可以看出上式一定是一个整数。

sample input

2 2

1 2 1 2 2点

2 1 2 1 1

sample output

2

@solution@

【说实话读完题目感觉整个人有些晕】

我们注意到题目中有这样一个句话:\((2^{n} E)\) 一定是整数。

为什么?我们考虑每一种完美匹配对答案的贡献。

假如我选用了 k 个组里面的边,凑出来了一个完美匹配。则剩下的组随便怎么出现,这一个完美匹配都会对答案产生贡献。也就是说,这一个完美匹配会让总期望增加 \(\frac{2^{m-k}}{2^m} = \frac{1}{2^k}\)。

可以发现 k <= n,因为完美匹配只需要 n 条边,而每个组最少都有 1 条边。

这样子,我们就可以完全抛开原本的概率期望,把这道题转换为组合计数问题。

对于 t = 0,因为每一组只会有一条边,所以我始终会选用 n 个组里面的边,相当于是统计完美匹配的个数。

这是一个很经典的二分图状压 dp。

对于 t = 1,假如最终的完美匹配只包含其中的一条边,则完全不影响;如果最终的完美匹配包含其中的两条边,它对答案的贡献就变成两倍。我们正常情况下已经贡献了一倍,则我们再进行一次特殊的转移,强制两条边一起选即可。

注意这个地方的状态定义为:\(dp[s1][s2]\) 表示二分图左边的状态为 s1,右边的状态。理论来说状态数非常大(155117520,这是在保证左右状态已经使用的点的数量相同下得到的理论状态数量),但是实测并不会用到多少。用记忆化搜索就可以避免无用状态。

对于 t = 2,我们容斥一下,用总的减去最终答案包含两条边的贡献。其实就是 t = 1 减去强制两条边一起选的贡献。

@accepted code@

#include<vector>
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN = 15;
const int MAXM = MAXN*MAXN;
const int MOD = int(1E9) + 7;
const int HASHSIZE = 1000037;
typedef pair<int, int> pii;
vector<pair<pii, int> >h[HASHSIZE];
void hash_insert(pii p, int k) {
int x = (1LL*p.first*13131%HASHSIZE + p.second)%HASHSIZE;
h[x].push_back(make_pair(p, k));
}
int hash_search(pii p) {
int x = (1LL*p.first*13131%HASHSIZE + p.second)%HASHSIZE;
for(int i=0;i<h[x].size();i++)
if( h[x][i].first == p ) return h[x][i].second;
return -1;
}
struct node{
int type, from, to;
node *lnk, *nxt;
}e[2*MAXM + 5], *adj[MAXN + 5], *ecnt=&e[0];
void add(int u, int v, int t, node *l) {
node *p = (++ecnt);
p->type = t, p->from = u, p->to = v, p->lnk = l;
p->nxt = adj[u], adj[u] = p;
}
int f[1<<MAXN];
int lowbit(int x) {
return x & -x;
}
int dfs(int s1, int s2) {
if( !s1 ) return 1;
pii p = make_pair(s1, s2);
int q = hash_search(p);
if( q != -1 ) return q;
else {
int k = f[lowbit(s1)], ans = 0;
for(node *a=adj[k];a;a=a->nxt) {
if( !a->type ) {
if( s2 & (1<<a->to) )
ans = (ans + dfs(s1^(1<<a->from), s2^(1<<a->to))) % MOD;
}
else {
node *b = a->lnk;
if( (s2 & (1<<a->to)) && (s2 & (1<<b->to)) )
ans = (ans + a->type*dfs(s1^(1<<a->from)^(1<<b->from), s2^(1<<a->to)^(1<<b->to)))%MOD;
}
}
hash_insert(p, ans);
return ans;
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i=0;i<n;i++)
f[1<<i] = i;
for(int i=1;i<=m;i++) {
int t, a1, b1, a2, b2;
scanf("%d", &t);
if( !t ) {
scanf("%d%d", &a1, &b1); a1--, b1--;
add(a1, b1, 0, NULL);
}
else {
scanf("%d%d%d%d", &a1, &b1, &a2, &b2); a1--, b1--, a2--, b2--;
add(a1, b1, 0, NULL); add(a2, b2, 0, NULL);
if( a1 != a2 && b1 != b2 ) {//注意如果相等,则两条边是不能够共存的。
add(a1, b1, t == 1 ? 1 : -1, ecnt + 2);
add(a2, b2, t == 1 ? 1 : -1, ecnt);
}
}
}
printf("%d\n", (dfs((1<<n)-1, (1<<n)-1) + MOD)%MOD);
}

@details@

为什么他们写 map 不写哈希能够过而我就过不了呢……

难道真的是因为我自带常数吗?

其实本代码在某个辣鸡 OJ 还是 TLE。

如果不说鬼才知道状态数原来很少。

@loj - 2290@ 「THUWC 2017」随机二分图的更多相关文章

  1. 【LOJ】#2290. 「THUWC 2017」随机二分图

    题解 看了一眼觉得是求出图对图统计完美匹配的个数(可能之前做过这样模拟题弃疗了,一直心怀恐惧... 然后说是统计一下每种匹配出现的概率,也就是,当前左边点匹配状态为S,右边点匹配状态为T,每种匹配出现 ...

  2. 「THUWC 2017」随机二分图

    「THUWC 2017」随机二分图 解题思路 : 首先有一个 \(40pts\) 的做法: 前 \(20pts\) 暴力枚举最终的匹配是怎样的,check一下计算方案数,后 \(20pts\) 令 \ ...

  3. LOJ 2288「THUWC 2017」大葱的神力

    LOJ 2288「THUWC 2017」大葱的神力 Link Solution 比较水的提交答案题了吧 第一个点爆搜 第二个点爆搜+剪枝,我的剪枝就是先算出 \(mx[i]\) 表示选取第 \(i \ ...

  4. @loj - 2289@「THUWC 2017」在美妙的数学王国中畅游

    目录 @description@ @solution@ @accepted code@ @details@ @description@ n 个点编号 0 到 n-1,每个点有一个从 [0,1] 映射到 ...

  5. @loj - 2288@「THUWC 2017」大葱的神力

    目录 @description@ @solution@ @data - 1@ @data - 2@ @data - 3@ @data - 4@ @data - 5@ @data - 6@ @data ...

  6. LOJ #2540. 「PKUWC 2018」随机算法(概率dp)

    题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...

  7. LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)

    写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...

  8. 「LOJ 2289」「THUWC 2017」在美妙的数学王国中畅游——LCT&泰勒展开

    题目大意: 传送门 给一个动态树,每个节点上维护一个函数为$f(x)=sin(ax+b)$.$f(x)=e^{ax+b}$.$f(x)=ax+b$中的一个. 支持删边连边,修改节点上函数的操作. 每次 ...

  9. 【LOJ】#2289. 「THUWC 2017」在美妙的数学王国中畅游

    题解 我们发现,题目告诉我们这个东西就是一个lct 首先,如果只有3,问题就非常简单了,我们算出所有a的总和,所有b的总和就好了 要是1和2也是多项式就好了--其实可以!也就是下面泰勒展开的用处,我们 ...

随机推荐

  1. Linux下安装docker,更改镜像仓库地址,并部署springboot应用

    今天做不成的事,明天也不会做好. 各位同学大家好,随着docker的快速发展,越来越多的人开始使用,一方面随着容器化这个趋势越来越火,docker成为了其中的佼佼者:二来容器化确实降低了运维的门槛,让 ...

  2. 2018-11-1-WPF-Main-thread-gets-a-deadlock-when-stylus-input-thread-is-waiting-for-the-window-to-clos...

    title author date CreateTime categories WPF Main thread gets a deadlock when stylus input thread is ...

  3. 大数据概念(4V)

  4. Leetcode47. Permutations II全排列2

    给定一个可包含重复数字的序列,返回所有不重复的全排列. 示例: 输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ] 在全排列1题目的基础上先排序,目的是把相同的 ...

  5. Vbulletin Used to Show Malicious Advertisements

    In the past, we have seen a massive amount of vBulletin websites compromised through theVBSeo Vulner ...

  6. 一个宽度不确定的DIV里放三个水平对齐的DIV,左右两个DIV宽度固定为100px,中间那个DIV自适应宽度

    方法一:浮动  注意三个div的位置 <html><head> <meta charset="utf-8"> <style type=&q ...

  7. (转)Cookie存中文乱码的问题

    有个奇怪的问题:登录页面中使用Cookie存值,Cookie中要存中文汉字.代码在本地调试,一切OK,汉字也能顺利存到Cookie和从Cookie中读出,但是放到服务器上不管用了,好好的汉字成了乱码, ...

  8. Statement对象

    Statement 对象 创建 Statement 对象 在你准备使用 Statement 对象执行 SQL 语句之前,你需要使用 Connection 对象的 createStatement() 方 ...

  9. git update-index --assume-unchanged忽略跟踪

    Git 忽略已跟踪文件的改动 git update-index --assume-unchanged Git之本地忽略 这个分两种情况: 本地永久忽略,效果的gitignore一样,只不过不适于写到g ...

  10. 用Direct2D和DWM来做简单的动画效果

    原文:用Direct2D和DWM来做简单的动画效果 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sunnyloves/article/detail ...