[ARC147E] Examination 题解

学了二分图的 Hall 定理,写一篇题解记录一下。

Hall 定理

一个二分图存在 完备匹配 ,当且仅当 \(V_1\) 中任意 \(k\) 个顶点至少连接 \(V_2\)\(k\) 个顶点。

这里就不加证明,读者可自行查阅资料。

题目大意

给定两个数组 \(A,B\),可以选择 \(A\) 的一些位置任意重排,使得对于任意的 \(i\) 都有 \(A_i \ge B_i\)

分析

现将 \(A,B\) 排序,本题可以转化为一个二分图的问题,如果有 \(A_j \ge B_i\),则 \(i,j\) 连边。

先把无解的情况判了,如果存在 \(A_i < B_i\) 则无解。证明是容易的,如果存在这个 \(i\),那么 \(i\) 以及其后面一共 \(n-i+1\) 个点在二分图中对应的右部点最多只有 \(n-i\) 个点,根据 Hall 定理,此时二分图不存在完备匹配,故无解。

接下来考虑最小化被选中重排的集合 \(S\),那么 \(A_i < B_i\) 的一定要包含在集合中,同时 \(S\) 以及对应的 \(N(S)\) 也应具有完备匹配。

于是我们先将 \(A_i < B_i\) 的提取出来做个排序,如果无法满足条件,则说明一定要从已经合法的一对提取出来,不难发现,我们想要的是 \(j\) 满足 \(B_j \le A_i\)\(A_j\) 最大。感性理解这样正确的原因,我们第一个约束满足了选出来的集合 \(S\) 一定有 \(A_i\ge B_i\),第二个约束能使得后面的决策能更容易地被满足,缩减 \(S\) 的规模。

具体地,我们使用两个小根堆维护二分图的左部点集合 \(V_1\),右部点集合 \(V_2\)。依次检索 \(V_1,V_2\) 所有对应元素,如果合法则跳过,否则选取未被选取的点中 \(B_j \le A_i\)\(A_j\) 最大的,这个可以依次加入并用大根堆维护。

时间复杂度,排序和堆都是 \(\Theta(n \log n)\) 的。

Code

#include <bits/stdc++.h>
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define ull unsigned long long
#define uint unsigned int
#define rg register
#define il inline
#define vint vector<int>
#define rep(i,a,b) for(rg int i=(a);i<=(b);++i)
#define sqr(x) ((x)*(x))
using namespace std;
using ll=long long;
const int INF=0x3f3f3f3f;
inline int read()
{
    int w=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+(ch^48);
        ch=getchar();
    }
    return w*f;
}
inline void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

const int N=3e5+10;
int n,a[N],b[N],tmpa[N],tmpb[N],cnt;
priority_queue<int,vector<int>,greater<int>> V1,V2;
vector<pii> stra;
priority_queue<int> Q;

bool cmp(const pii &a,const pii &b)
{
    return a.second<b.second;
}

int main()
{
    #ifndef ONLINE_JUDGE
    //freopen("in.txt","r",stdin);
    #endif
    cnt=n=read();
    for(int i=1;i<=n;++i) tmpa[i]=a[i]=read(),tmpb[i]=b[i]=read();
    sort(tmpa+1,tmpa+n+1),sort(tmpb+1,tmpb+n+1);
    for(int i=1;i<=n;++i)
    {
        if(tmpa[i]<tmpb[i])
        {
            cout<<-1<<endl;
            return 0;
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(a[i]<b[i])
        {
            V1.push(a[i]),V2.push(b[i]);
        }
        else
        {
            stra.push_back(make_pair(a[i],b[i]));
        }
    }
    sort(all(stra),cmp);
    auto it=stra.begin();
    while(!V1.empty())
    {
        --cnt;
        int va=V1.top(),vb=V2.top();
        if(va>=vb)
        {
            V1.pop(),V2.pop();
        }
        else
        {
            while(it!=stra.end()&&it->second<=va)
            {
                Q.push(it->first);
                ++it;
            }
            int now=Q.top();
            Q.pop(),V1.pop(),V1.push(now);
        }
    }
    cout<<cnt<<endl;
    return 0;
}

不难发现无解的情况当且仅当 \(V_1,V_2\) 不合法且备选决策集合为空,所以有不用排序的代码如下。

#include <bits/stdc++.h>
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define ull unsigned long long
#define uint unsigned int
#define rg register
#define il inline
#define vint vector<int>
#define rep(i,a,b) for(rg int i=(a);i<=(b);++i)
#define sqr(x) ((x)*(x))
using namespace std;
using ll=long long;
const int INF=0x3f3f3f3f;
inline int read()
{
    int w=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+(ch^48);
        ch=getchar();
    }
    return w*f;
}
inline void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

const int N=3e5+10;
int n,a[N],b[N],cnt;
priority_queue<int,vector<int>,greater<int>> V1,V2;
vector<pii> stra;
priority_queue<int> Q;

bool cmp(const pii &a,const pii &b)
{
    return a.second<b.second;
}

int main()
{
    #ifndef ONLINE_JUDGE
    //freopen("in.txt","r",stdin);
    #endif
    cnt=n=read();
    for(int i=1;i<=n;++i) a[i]=read(),b[i]=read();
    for(int i=1;i<=n;++i)
    {
        if(a[i]<b[i])
        {
            V1.push(a[i]),V2.push(b[i]);
        }
        else
        {
            stra.push_back(make_pair(a[i],b[i]));
        }
    }
    sort(all(stra),cmp);
    auto it=stra.begin();
    while(!V1.empty())
    {
        --cnt;
        int va=V1.top(),vb=V2.top();
        if(va>=vb)
        {
            V1.pop(),V2.pop();
        }
        else
        {
            while(it!=stra.end()&&it->second<=va)
            {
                Q.push(it->first);
                ++it;
            }
            if(Q.empty())
            {
                cout<<-1<<endl;
                return 0;
            }
            int now=Q.top();
            Q.pop(),V1.pop(),V1.push(now);
        }
    }
    cout<<cnt<<endl;
    return 0;
}
posted @ 2025-04-23 20:33  vanueber  阅读(36)  评论(0)    收藏  举报