「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\)
我們可以用線段樹來維護(相當於是 \(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; }