• 【CF981F】Round Marriage(二分答案,hall定理)


    传送门

    题意:
    给出一个长度为(L)的环,标号从(0)(L-1)
    之后给出(n)个新郎,(n)个新娘离起点的距离。
    现在新郎、新娘要一一配对,但显然每一对新人的产生都会走一定的距离(d_i),求所有(d_i)中最大值最小是多少。

    思路:

    • 显然最后的答案具有单调性,故可以二分答案之后来判定。
    • 二分最大时间(x),那么只添加距离不超过(x)的边,做个最大匹配即可。
    • 但因为(n)达到(2e5),显然匈牙利算法不可行。
    • 考虑(hall)定理:若一个二分图存在完美匹配,那么对于左边任意子集(S),其对应边连接了一个点集(T),那么有(|S|leq |T|)
    • (hall)定理考虑的是集合,不好处理。这个题的二分图有一个特殊的地方,就是对于左边的一个点(i),连接的一定是一段连续的区间([l_i,r_i])
    • 根据(hall)定理,我们要判断不可行的话就需要找到一个点集(S),最终(|S|>|T|)。考虑极小的一个子集(|S|)满足上述条件,也就是此时去掉任何一个点都不满足条件了,此时这个点集一定是连续的一些点,因为二分图的特殊性。
    • 所以问题由点集转化为了:在二分图中,若有(r-l>R_r-L_l),则不满足条件,移下项就有:(r-R_r>l-L_l),然后随便维护一下即可。

    感觉(hall)定理在某些情况下挺好用的?尤其是二分图比较特殊的情况,用来求最大匹配/判断最大匹配挺优秀的,通常都把问题转换成维护信息的问题。

    代码实现将环变成链时有点细节,详见代码:

    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/5 19:43:44
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 2e5 + 5;
    int n, L;
    ll a[N << 2], b[N << 2];
     
    bool chk(int x) {
        int p1 = 1, p2 = 1;
        int mn = INF;
        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;
            mn = min(mn, i - p1);
            int now = i - p2 + 1;
            if(mn < now) return false;
        }
        return true;
    }
     
    void run(){
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i++) cin >> b[i];
        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 = INF, mid;
        while(l < r) {
            mid = (l + r) >> 1;
            if(chk(mid)) r = mid;
            else l = mid + 1;
        }
        cout << l << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n >> L) run();
    	return 0;
    }
    
  • 相关阅读:
    重排序
    线程的生命周期状态
    并发的有序性
    并发的可见性
    并发的原子性
    Java内存模型
    缓存一致性协议MESI
    lsof
    nmap
    elastcisearch简介
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11809130.html
Copyright © 2020-2023  润新知