hall定理相关
匹配:就是在图上找到端点不交的边集。
最大匹配:能够覆盖的点集最大。
完备匹配:能够覆盖某侧所有点。完备匹配一定是最大匹配,最大匹配不一定是完备匹配。
完美匹配:能够覆盖所有点。完美匹配一定是完备匹配,完备匹配不一定是完美匹配。
hall 定理
对于一个二分图 \(G(V1,V2,E)\) 钦定 \(|V1|\le|V2|\),对于 \(\forall X\subseteq V1\),定义 \(N(X)=\{v_j|(v_i,v_j)\in E,v_i\in X,v_j\in V2\}\)。
其存在 \(V1\) 的完备匹配的充要条件为 \(\forall X\subseteq V1,|X|\le |N(X)|\)。
简单的说,就是 \(V1\) 的任意子集 \(X\) 向 \(V2\) 连边,连到的所有点形成的集合 \(N(X)\) 的大小大于等于 \(X\) 的大小。
考虑数学归纳法证明。
对 \(V1\) 大小为 \(n\)。假设任取大小为 \(k<n\) 的子集 \(X\),满足 \(N(X)\ge k+1\),那么去除任意一对匹配后,剩下的点只要满足 hall 定理,就可以根据 \(n-1\) 说明 \(n\) 时满足 hall 定律。而这是显然的,因为较劣的情况是 \(V1\) 的大小不变而 \(V2\) 可连集合大小减一,这仍然满足 hall 定理。
假设存在大小为 \(k\) 的子集 \(X\),满足 \(N(X)=k\),那么将这 \(k\) 对匹配去掉后,剩下的点也满足 hall 定理,和上面类似。
扩展1
二分图最大匹配大小为 \(n-a\) 当且仅当任意 \(X\) 满足 \(|X|-a\le |N(X)|\)。数学归纳法证明和上面类似。
也就是说,一张二分图的最大匹配大小为 \(n+min(|N(X)|-|X|)\)。
扩展2
要求每个点向右连 \(a_i\) 个匹配(?),当且仅当任意 \(X\) 满足 \(\sum\limits_{i\in X}a_i\le |N(X)|\)。把左边每个点拆成 \(a\) 个即可。
应用时总是需要建模技巧。
例题的难度大概是递减
例题hdu 5503
\(n\) 个队伍两两比赛,胜者加 \(1\) 分。给出最终的得分序列,问这样的得分序列是否可能存在。
每个队伍需要得 \(a_i\) 分,而每个比赛可以给两个队伍中一个加 \(1\) 分,先判断总数是否合法。
可以看作一张二分图,每个队伍向比赛连边,队伍最多连 \(a\) 条,比赛最多连两条,判断是否存在完美匹配。这就是上文中提到的扩展 2 的情况,我们将每个队伍拆成 \(a\) 个点后对比赛用 hall 定理,于是对于任意 \(\dbinom k2\) 个比赛,对应左边的 \(k\) 个队伍,则 \(\sum\limits_{i\in k} a_i\ge \dbinom k2\),只需把 \(a_i\) 排序选最小的 check。
其实有时会让人想到 2-set,2-set 需要把每个位置对另一个位置的可选状态都看作点,即使有优化建图的方法,过多的点数和边数仍是 2-set 的瓶颈。
\(n\) 组人在长为 \(m\) 的环上开会,每组有 \(a_i\) 个人需要安排到 \([l_i,r_i]\) 上,问是否存在完备匹配。\(n\le 10^5,m\le 10^9\)。
环上先考虑链。对每组用 hall 定理,考虑到区间不相邻的两组合并考虑对判断无影响,所以相当于只需考虑所有有交的区间并。再反向考虑(hall 定理仍然正向使用),只需对任意 \([L,R]\),满足所有被 \([L,R]\) 完全包含的 \([l_i,r_i]\) 的 \(\sum a_i\le R-L+1\)。
由于 \(L,R\) 只会出现在 \(l,r\) 中,于是 \(\sum a_i+L-1\le R\) 只需枚举 \(R_i\) 并对 \(L\in [1,R_i)\) 求 \(\sum a+L-1\) 的最大值,再用 \(+a_i\) 更新 \(L\in[1,L_i]\) 即可。
对于环,拆环为链,但有一个不容易注意到的问题。就是当 \(r=l-1\) 时,它本来应该包含所有区间,但如果某个 \(L,R\) 满足 \(L\le l,R\ge r\),那么这个 \((L,R)\) 和复制的 \((L+m,R+m)\) 都没有计算成被包含。所以对于整个区间需要特判。
这个更适合入门?
所有 \(a_i\) 向 \(b_i\ge h-a_i\) 连边,判断完美匹配。发现把 \(b\) 排序后 \(a\) 连的是一段后缀,所以我们只需要判断 \(b_1\) 连了大于等于 \(1\) 条,\(\cdots b_{m-1}\) 连了大于等于 \(m-1\) 条,\(b_m\) 连了大于等于 \(m\) 条。
那么我们可以给 \(b_1\cdots,b_m\) 赋初值 \(-1\cdots,-m\),然后每次区间加减并判断最小值的正负即可。