「JOISC 2016 Day 4」最差记者 2
「JOISC 2016 Day 4」最差记者 2
考虑一个小贪心。
将 \(2\) 小时和 \(5\) 小时的节点放在一起按分数从小到大排序,如果分数相同则将 \(2\) 小时的节点放在前面。
然后对于每个\(5\) 小时节点,我们找到未匹配的且分数最大的且国籍相同的 \(2\) 小时节点并且将它与该节点匹配。
但是我们会发现一个问题。这么做了之后剩下的点可能不能够互相匹配。
否则,那么答案就是 剩下的点数 \(/\;2\) 。
我们考虑魔改贪心过程,使得让贪心过程中保证剩下的点能够互相匹配。
这样答案就是 剩下的点数 \(/\;2\) 了。
我们注意到这个问题可以抽象成二分图最大匹配的模型。
所以我们可以考虑 Hall 定理
对于一个二部图 \(G(X,Y)\),\(X\) 存在一个匹配的充分必要条件为对于 \(X\) 的任意子集 \(S\),\(S\) 的邻居个数 \(N(S)\) 必须大于等于 \(S\) 的大小 \(|S|\)。
注意到原图有点特殊,对于一个点 \(x\) 来说,所有排序后在他之前,且不是跟他一个类型(就是不是都是 \(2\) 小时或 \(5\) 小时的点)的点都跟他有边。也就是一个点的所连的点集是它的前缀。
所以对于 \(X\) 任意一个子集 \(S\) 来说,\(N(S)\) 就等于 \(S\) 中分最大的那个点的边集。
所以所有的 \(N(S)>0\) 相当于是对于排序后的每个点 \(i\) 。在 \(i\) 之前存在的 \(X\) 部的点的数量与 \(Y\) 部的点的数量要相同。
我们可以用线段树来维护(相当于是 \(X\) 部的点权为负,\(Y\) 部点权为正,询问前缀最小值是否小于 \(0\) ,让线段树支持区间查询最值和区间修改即可)。
每次加入一个 \(5\) 小时的点,判断和它同国籍且分数最大的点跟他匹配后还存不存在完全匹配。
如果存在就让他们匹配,否则不让他们匹配,并在线段树中更新。
正确性显然。
时间复杂度为 \(O(n\log n)\)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5+5;
struct Tree
{
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
int t[MAXN<<2],tg[MAXN<<2];
Tree(){memset(t,0,sizeof t);memset(tg,0,sizeof tg);}
void pu(int k){t[k]=min(t[ls(k)],t[rs(k)]);}
void pd(int k)
{
if(!tg[k]) return ;
t[ls(k)]+=tg[k];t[rs(k)]+=tg[k];
tg[ls(k)]+=tg[k];tg[rs(k)]+=tg[k];
tg[k]=0;
}
void upd(int le,int ri,int k,int l,int r,int x)
{
if(le<=l&&r<=ri)
{
tg[k]+=x;
t[k]+=x;
return ;
}
int mid=l+r>>1;pd(k);
if(le<=mid) upd(le,ri,ls(k),l,mid,x);
if(ri>mid) upd(le,ri,rs(k),mid+1,r,x);
pu(k);
}
int que(int le,int ri,int k,int l,int r)
{
if(le<=l&&r<=ri) return t[k];
int mid=l+r>>1,ans=1e9;pd(k);
if(le<=mid) ans=min(ans,que(le,ri,ls(k),l,mid));
if(ri>mid) ans=min(ans,que(le,ri,rs(k),mid+1,r));
return ans;
}
}T;
struct node
{
int s,p,opt;
bool operator < (const node &x)const
{
return s!=x.s?s<x.s:opt<x.opt;
}
}A[MAXN];
int n;
stack<int> st[MAXN];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
int p,s;
scanf("%d %d",&p,&s);
A[i]=node{s,p,0};
}
for(int i=1;i<=n;++i)
{
int p,s;
scanf("%d %d",&p,&s);
A[i+n]=node{s,p,1};
}
n*=2;
sort(A+1,A+1+n);
int ans=n/2;
for(int i=1;i<=n;++i)
{
if(A[i].opt==0)
{
st[A[i].p].push(i);
T.upd(i,n,1,1,n,1);
}
else
{
if(st[A[i].p].empty())
{
T.upd(i,n,1,1,n,-1);
continue;
}
int p=st[A[i].p].top();
T.upd(p,n,1,1,n,-1);
if(T.que(1,i,1,1,n)<0)
{
T.upd(p,n,1,1,n,1);
T.upd(i,n,1,1,n,-1);
}
else ans--,st[A[i].p].pop();
}
}
printf("%d\n",ans);
return 0;
}