【题解】Atcoder ARC#76 F-Exhausted?

  第一次用霍尔定理做题..简单的来说,就是判断一张二分图上是否存在完美匹配,只需要证明对于 \(a\) 集合中的任意 \(k\) 个点来说,都与 \(b\) 集合中的 \(k\) 个点有边相连。如果不满足,那么最大匹配数就是两个集合中有连边的点数最大的差。

  这道题目二分图匹配的解法是非常显然的,让 \(i\) 点和对面的 \(1 ->  l[i]\), \(r[i]  ->  m\) 点连边,判断是否存在完美匹配即可。但点数太多了,我们考虑使用霍尔定理来求解。如果我们固定右边选择的点为 \(1->l[i]\), \(r[i] -> m\),那为了判断是否满足我们自然要尽量地使得左边的点数更大(如果在最大情况下依然合法,也就说明的确是存在完美匹配的,当然这也提示我们霍尔定理就是要寻找题目的特殊性质,固定一边的点数来考虑最坏的情况)。要使左边的点数最大,显然我们应该选入所有 \(l -> r\) 在这个范围内的点。所以我们可以使用扫描线降维维护最值。(~ ̄▽ ̄)~

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000000
#define INF 99999999
int n, m, mn[maxn], mark[maxn];
int ans = INF;

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

struct node
{
    int l, r;
    friend bool operator <(const node& a, const node& b)
    { return a.r > b.r; }
}P[maxn];

void push_down(int p)
{
    if(!mark[p]) return;
    mark[p << 1] += mark[p], mark[p << 1 | 1] += mark[p];
    mn[p << 1] += mark[p], mn[p << 1 | 1] += mark[p];
    mark[p] = 0;
}

void Update(int p, int l, int r, int L, int R, int x)
{
    if(L <= l && R >= r) { mn[p] += x; mark[p] += x; return; }
    if(L > r || R < l) return;
    int mid = (l + r) >> 1;
    push_down(p);
    Update(p << 1, l, mid, L, R, x); 
    Update(p << 1 | 1, mid + 1, r, L, R, x);
    mn[p] = min(mn[p << 1], mn[p << 1 | 1]);
}

void Build(int p, int l, int r)
{
    if(l == r) { mn[p] = l; return; }
    int mid = (l + r) >> 1;
    Build(p << 1, l, mid), Build(p << 1 | 1, mid + 1, r);
    mn[p] = min(mn[p << 1], mn[p << 1 | 1]); 
}

int Query(int p, int l, int r, int x)
{
    if(l == r) return mn[p];
    int mid = (l + r) >> 1;
    push_down(p);
    if(x <= mid) return Query(p << 1, l, mid, x);
    else return Query(p << 1 | 1, mid + 1, r, x);
}

int main()
{
    n = read(), m = read(); 
    for(int i = 1; i <= n; i ++) P[i].l = read(), P[i].r = read();
    sort(P + 1, P + 1 + n);
    int now = 1; Build(1, 0, m);
    for(int i = m + 1; i >= 0; i --)
    {
        while(now <= n && P[now].r >= i) 
            Update(1, 0, m, P[now].l, m, -1), now ++;
        Update(1, 0, m, i, m, -1);
        ans = min(ans, (m - i + 1) + mn[1]);
    }
    if(ans < 0) printf("%d\n", -ans);
    else printf("0\n");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/twilight-sx/p/9693439.html