1. 程式人生 > 其它 >「JOISC 2016 Day 4」最差記者 2

「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;
}