CF981F Round Marriage
写在前面
想了两天的题,感觉还是……雾
所以下面直接给出了结论和 cjk 学长的证明,感谢 cjk 学长又讲了一遍 =
环上有 \(n\) 个黑点,\(n\) 个白点。
求一组黑白点的匹配使得匹配中距离最大的一对点距离最小。
距离定义为在环上从两个方向走的最短距离
\(n\leq 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_i\geq j-i\)
移个项得 \(R_j-j\geq 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;
}