將所有$A$和$B$混在一起排序,那麼每個$B$要匹配一個$A$,從左往右依次考慮每個數:
如果是一個$B$:
- 如果左邊沒有多餘的$A$,那麼將其放入堆$q_C$中,表示這個$B$還未匹配。
- 否則選擇左邊代價最小的$A$和這個$B$進行匹配,並把代價取反,加上這個$B$往右匹配的貢獻後放入堆$q_B$中,表示未來某個$A$搶走這個$B$的代價,即費用流的反悔操作。
如果是一個$A$:
- 如果$q_C$非空,那麼將該$A$直接與$q_C$中代價最小的$B$進行匹配即可。
- 否則如果$q_B$非空且搶走之前的某個$B$更優,那麼搶走之前的$B$,並把這個代價取反,加上這個$A$往右匹配的貢獻後放入堆$q_A$中,同理表示費用流的反悔操作。
- 否則說明這個$A$暫時不需要和左邊某個$B$進行匹配,將其放入待命區,也就是堆$q_A$中即可。
顯然每個數只會進行$O(1)$次堆操作,故時間複雜度為$O((n+m)\log (n+m))$。
#include<cstdio> #include<queue> #include<vector> #include<algorithm> using namespace std; typedef long long ll; int Case,n,m,i,ce,x;ll ans; struct E{int x,y;E(){}E(int _x,int _y){x=_x,y=_y;}}e[200010]; inline bool cmp(const E&a,const E&b){return a.x<b.x;} int main(){ scanf("%d",&Case); while(Case--){ priority_queue<ll,vector<ll>,greater<ll> >A,B,C; scanf("%d%d",&n,&m); ce=0; while(n--){ scanf("%d",&x); e[++ce]=E(x,0); } while(m--){ scanf("%d",&x); e[++ce]=E(x,1); } sort(e+1,e+ce+1,cmp); ans=0; for(i=1;i<=ce;i++){ ll x=e[i].x; if(e[i].y==0){ if(!C.empty()){ ll t=C.top(); C.pop(); ans+=x+t; }else if(!B.empty()){ ll t=B.top(); if(t+x<0){ B.pop(); ans+=x+t; A.push(-t-x-x); }else A.push(-x); }else A.push(-x); }else{ if(!A.empty()){ ll t=A.top(); A.pop(); ans+=x+t; B.push(-t-x-x); }else C.push(-x); } } printf("%lld\n",ans); } }