CF981F Round Marriage

题面

写在前面

想了两天的题,感觉还是……雾

所以下面直接给出了结论和 cjk 学长的证明,感谢 cjk 学长又讲了一遍 =

环上有 (n) 个黑点,(n) 个白点。

求一组黑白点的匹配使得匹配中距离最大的一对点距离最小。

距离定义为在环上从两个方向走的最短距离

(nleq 2*10^5)

solution

显然,答案具有二分性。

然后点对之间连接合法的边,看白点和黑点是否为完美匹配就好了

怎么判断是否为完美匹配??

  • 二分完答案之后,暴力连边,跑匈牙利,看最大匹配是否为 (n) 就好

显然为 T,考虑怎么快速判断是否为完美匹配 ?

根据 (hall)​ 定理: 若一个二分图存在完美匹配,那么对于左边任意子集 (S) ,其对应边连接了一个点集 (T),那么有 (|S|≤|T|)

考虑这个二分图,它有一个特殊的性质:对于一个点,它所连向的右边的点都是连续的(右边的点按照坐标排好序)

根据 (hall) 定理,如果不是完美匹配,那么就要找一个点集 (|S|) 使得 (|S|>|T|)

一个结论:如果一个连续的黑点 (白点) 区间不满足hall定理,那么整个图就不满足 hall 定理

对于一个黑点 (i)​,其匹配的区间为 ([L_i,R_i])​ ,那么对于任意的两个黑点 (i, j)((i < j)) 必须满足 (R_j-L_igeq j-i)

移个项得 (R_j-jgeq L_i-i)

单调队列维护整个东西就好

关于上面那个结论的证明:(来自 cjk 学长)

现在你考虑选了 (k) 个黑点,如果满足 (k) 各黑点对应的白点的数 < k, 则不满足hall 定理

先证白点是连续的,如果把白点看做若干联通块,那么一定可以从中选出一个联通块出来,使这个连通块不满足 (hall) 定理,那这个白点连通块是(k) 个黑点其中某些点可以匹配的所有白点

一个黑点能匹配的白点是一个区间,而且如果把黑点按坐标排序,它对应的可以匹配的白点的区间的左右端点也是不降的

所以如果选的黑点不连续,假设把中间漏掉的黑点也选上,这些黑点可能匹配的白点集合不会增加,原来黑点不连续的时候就已经满足 k> 对应的白点了

k 现在变大了,白点数不变,所以依旧是不满足 (hall) 定理的,所以只用考虑坐标连续的若干个黑点即可

(code)

/*
work by:Ariel_
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#define rg register
#define int long long 
using namespace std;
const int N = 2e5 + 5;
int read(){
    int x = 0,f = 1; char c = getchar();
    while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    return x*f;
}
int a[N << 2], b[N << 2], n, L;
bool check(int x) {
   int p1 = 1, p2 = 1, Max = -0x3f3f3f3f;
   for (int i = 1; i <= 2 * n; i++) {
   	   while (p1 <= 4 * n && b[p1] < a[i] - x) ++p1;
   	   while (p2 <= 4 * n && b[p2] <= a[i] + x) ++p2;
   	   Max = max(Max, p1 - i);
   	   int now = p2 - i - 1;
   	   if (Max > now) return false; 
   }
   return true;
}
signed main(){
   n = read(), L = read();
   for (int i = 1; i <= n; i++) a[i] = read();
   for (int i = 1; i <= n; i++) b[i] = read();
   sort(a + 1, a + n + 1), sort(b + 1, b + n + 1);
   for (int i = 1; i <= n; i++) a[i] += L, a[i + n] += a[i] + L;
   for (int i = 1; i <= 3 * n; i++) b[i + n] = b[i] + L;
   int l = 0, r = L / 2, ans;
   while(l < r) {
   	 int mid = (l + r) >> 1;
   	 if (check(mid)) r = mid;
   	 else l = mid + 1;
   } 
   printf("%lld", l);
   puts("");
   return 0;
}
原文地址:https://www.cnblogs.com/Arielzz/p/15041242.html