luogu 4698[CEOI2011]Hotel

題意:一個旅館,nn個房間,每個房間可容納人數爲pip_i,維護費用爲cic_i。現在有mm個訂單,每個訂單消費金額爲viv_i,人數爲did_i。每個訂單被接待了以後你需要安排一個房間,房間容量大於等於人數,你可以得到消費金額減維護費用的收益。問在接收oo個以內訂單的情況下最大收益。
數據範圍:1n,m5000001\leq n,m \leq 5000001omin(n,m)1\leq o \leq min(n,m)1pi,vi,ci,di1091 \leq p_i , v_i, c_i ,d_i \leq 10^9,保證i,j\forall i,j,如果pi<pjp_i<p_jcicjc_i\leq c_j,時限40004000ms

解法:我們發現n,m,on,m,o全特別大,n3n^3dp是一定過不了了,這個時候我們用到一個常用的套路,把oo變成一個loglog。具體做法就是每次選訂單的時候把代價減一個常數,再記錄訂單總數。顯然的是這個常數越大,訂單數越小
。我們能這麼做是因爲對於每個oo,答案是上凸的。因爲對於較小的oo,沒選的訂單中比較優的一定更大。
現在oo省掉了,就是一個nnmm的匹配問題,經典的貪心,因爲p,cp,c滿足偏序關係,我們從小到大考慮pp,每次只要有最大的vv使得它有收益就匹配。用大根堆維護。
時間複雜度:O(nlog(max(v))log(n))O(n log(max(v) ) log(n))
代碼

#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章