「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;
}
posted @ 2022-03-29 21:07  夜空之星  阅读(138)  评论(0编辑  收藏  举报