题意:一个旅馆,
n
n
n个房间,每个房间可容纳人数为
p
i
p_i
pi,维护费用为
c
i
c_i
ci。现在有
m
m
m个订单,每个订单消费金额为
v
i
v_i
vi,人数为
d
i
d_i
di。每个订单被接待了以后你需要安排一个房间,房间容量大于等于人数,你可以得到消费金额减维护费用的收益。问在接收
o
o
o个以内订单的情况下最大收益。
数据范围:
1
≤
n
,
m
≤
500000
1\leq n,m \leq 500000
1≤n,m≤500000,
1
≤
o
≤
m
i
n
(
n
,
m
)
1\leq o \leq min(n,m)
1≤o≤min(n,m),
1
≤
p
i
,
v
i
,
c
i
,
d
i
≤
1
0
9
1 \leq p_i , v_i, c_i ,d_i \leq 10^9
1≤pi,vi,ci,di≤109,保证
∀
i
,
j
\forall i,j
∀i,j,如果
p
i
<
p
j
p_i<p_j
pi<pj则
c
i
≤
c
j
c_i\leq c_j
ci≤cj,时限
4000
4000
4000ms
解法:我们发现
n
,
m
,
o
n,m,o
n,m,o全特别大,
n
3
n^3
n3dp是一定过不了了,这个时候我们用到一个常用的套路,把
o
o
o变成一个
l
o
g
log
log。具体做法就是每次选订单的时候把代价减一个常数,再记录订单总数。显然的是这个常数越大,订单数越小
。我们能这么做是因为对于每个
o
o
o,答案是上凸的。因为对于较小的
o
o
o,没选的订单中比较优的一定更大。
现在
o
o
o省掉了,就是一个
n
n
n和
m
m
m的匹配问题,经典的贪心,因为
p
,
c
p,c
p,c满足偏序关系,我们从小到大考虑
p
p
p,每次只要有最大的
v
v
v使得它有收益就匹配。用大根堆维护。
时间复杂度:
O
(
n
l
o
g
(
m
a
x
(
v
)
)
l
o
g
(
n
)
)
O(n log(max(v) ) log(n))
O(nlog(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