題意:一個旅館,個房間,每個房間可容納人數爲,維護費用爲。現在有個訂單,每個訂單消費金額爲,人數爲。每個訂單被接待了以後你需要安排一個房間,房間容量大於等於人數,你可以得到消費金額減維護費用的收益。問在接收個以內訂單的情況下最大收益。
數據範圍:,,,保證,如果則,時限ms
解法:我們發現全特別大,dp是一定過不了了,這個時候我們用到一個常用的套路,把變成一個。具體做法就是每次選訂單的時候把代價減一個常數,再記錄訂單總數。顯然的是這個常數越大,訂單數越小
。我們能這麼做是因爲對於每個,答案是上凸的。因爲對於較小的,沒選的訂單中比較優的一定更大。
現在省掉了,就是一個和的匹配問題,經典的貪心,因爲滿足偏序關係,我們從小到大考慮,每次只要有最大的使得它有收益就匹配。用大根堆維護。
時間複雜度:
代碼
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=5e5+7;
const ll INF=1e9+7;
struct pir{
ll v,d;
}p1[N],p2[N];
ll n,m,o;
priority_queue<ll>q;
bool cmp(pir x,pir y){
if(x.d==y.d)return x.v<y.v;
return x.d<y.d;
}
pir check(ll x,bool f){
ll nw=0,ans=0,c=0;
while(!q.empty())q.pop();
rep(i,n){
while(nw!=m&&p2[nw+1].d<=p1[i].d)nw++,q.push(p2[nw].v);
if(!q.empty()){
if(q.top()-x-p1[i].v>0){
ans+=q.top()-x-p1[i].v;
c++;
q.pop();
}
else if(q.top()-x-p1[i].v==0&&f)c++,q.pop();
}
}
return (pir){ans,c};
}
int main(){
scanf("%lld%lld%lld",&n,&m,&o);
rep(i,n)scanf("%lld%lld",&p1[i].v,&p1[i].d);
rep(i,m)scanf("%lld%lld",&p2[i].v,&p2[i].d);
sort(p1+1,p1+n+1,cmp);
sort(p2+1,p2+m+1,cmp);
ll L=0,R=INF;
pir t=check(0,0);
if(t.d<=o){printf("%lld\n",t.v); return 0;}
while(L<R-1){
ll mid=(L+R)>>1;
if(check(mid,1).d>=o)L=mid;
else R=mid;
}
if(check(L,0).d<=o&&check(L,1).d>=o)printf("%lld\n",check(L,0).v+L*o);
else printf("%lld\n",check(R,0).v+R*o);
return 0;
}
大根堆常數較大,T了兩回,懶得手寫的我無恥的開了O2