@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. fastjson 对象和json互转

    list转json List<Openid> openids = od.getAll(session); String json = JSONObject.toJSONString(ope ...

  2. [jnhs]id字段修改错误导致hibernate hql查询整表只返回第一条数据

    调试发现,查询到的就是一条数据 hql语句执行结果 Hibernate: select ballmodel0_.ball_id as ball_id1_1_, ballmodel0_.color as ...

  3. IO流8 --- 使用FileReader和FileWriter实现文本文件的复制 --- 技术搬运工(尚硅谷)

    @Test public void test4(){ FileReader fr = null; FileWriter fw = null; try { fr = new FileReader(&qu ...

  4. linux系统RabbitMQ启动错误记录

    安装并配置好RabbitMq之后终端执行rabbitmq-server报错 试了网上的各种方法也无济于事 最后发现可能是因为访问权限的问题(并不确定) 解决方法:sudo rabbitmq-serve ...

  5. DOM 事件监听 事件冒泡 事件捕获

    addEventListener() 方法 实例: // 当用户点击按钮时触发监听事件: document.getElementById("myBtn").addEventList ...

  6. 【JZOJ3624】【SDOI2014】数数(count) AC自动机+数位dp

    题面 100 容易想到使用AC自动机来处理禁忌子串的问题: 然后在自动机上数位dp,具体是: \(f_{i,j,0/1}\)表示填了\(i\)位,当前在自动机的第\(j\)个结点上,\(0\)表示当前 ...

  7. <第一周>降维

    PCA 矩阵的主成分就是其协方差矩阵对应的特征向量,按照对应的特征值大小进行排序,最大的特征值为第一主成分,以此类推 主要过程 对所有样本进行中心化 计算样本的协方差矩阵 XX.T 对协方差矩阵做特征 ...

  8. GBRT(GBDT)(MART)(Tree Net)(Tree link)

    源于博客 GBRT(梯度提升回归树)有好多名字,标题全是它的别名. 它是一种迭代的回归树算法,由多棵回归树组成,所有树的结论累加起来得到最终结果.在被提出之初与SVM一起被认为是泛化能力较强的算法. ...

  9. JavaScript--关于实例对象带不带参数和构造函数带不带参数的关系

    就是一句话: 构造函数创建对象时,也可以带参数,因为可以对对象进行一些属性的初始化,也就是你创建对象时,就带着这些属性,当然你也可以不带参数,后面实例化对象后再进行添加.而且,js函数的参数在定义函数 ...

  10. MAC使用技巧之苹果电脑抓图截屏方法

    用苹果电脑自带的截图功能的快捷键:command+shift+3 三个键按下则抓取/截取全屏 command+shift+4 然后用鼠标框选则抓取该区域的截图 command+shift+4 然后按空 ...