zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    小学生双倍快乐
    暑期随笔
    akm
    设计原型
    测试与优化
    小学生求求你别折磨程序员
    小学生快乐刷题
    枪在手,跟我走

    第一次团队作业
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15041242.html
Copyright © 2011-2022 走看看