P11268 MX-S5-T2 题解
建模
首先建出费用流模型。
- 源点
S 向所有物品连边(1,0) 。(前者为容量,后者为费用,下同) - 每个物品
i 向表示折扣价购买的点B 连边(1,b_i) 。 - 每个物品
i 向所有自己可以用的优惠券j 连边(1,a_i-v_j) 。 -
- 每个优惠券向汇点
T 连边(1,0) 。
答案就是最小费用最大流的费用。
模拟费用流
在这张图上模拟寻找增广路径的过程。我们管物品到
一、走一类边,费用为
二、走二类边,费用为
三、走二类边,再走二类反边,再走一类边,费用为
四、走二类边,再走二类反边,再走另一条二类边。费用为
优化增广过程
首先我们发现第四类增广是没用的。因为如果有第四类增广,则需要已经存在流
现在只需要考虑前三种了。但是好像还是不太好去维护我们每次最优的增广方案是什么。
注意到(注意力惊人)如果我们把物品按照
首先一定会增广
接下来考虑两个物品
对于第一类增广,由于
对于第二类增广,
对于第三类增广费用为
综上,不管哪种增广方式,如果
于是我们可以将物品先排序,然后对于物品
代码
#include<bits/stdc++.h>
using namespace std;
constexpr int N=1e6+5;
typedef long long ll;
typedef pair<int,int> pii;
#define fi first
#define se second
int n,m;
pii item[N],coupon[N];//first=a,w second=b,v;
priority_queue<ll> vw,ab;
int match[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>item[i].fi>>item[i].se;
for(int i=1;i<=m;i++)cin>>coupon[i].fi>>coupon[i].se;
sort(item+1,item+1+n);
sort(coupon+1,coupon+1+m);
int ptr=1;
ll ans=0;
for(int i=1;i<=n;i++){
while(ptr<=m&&coupon[ptr].fi<=item[i].fi){
vw.push(coupon[ptr].se);
ptr++;
}
ll w1=item[i].se;
ll w2=vw.empty()?1ll<<40:item[i].fi-vw.top();
ll w3=ab.empty()?1ll<<40:item[i].fi-ab.top();
ll mn=min({w1,w2,w3});
if(mn==w1)ans+=w1;
else if(mn==w2){
ans+=w2;
ab.push(item[i].fi-item[i].se);
vw.pop();
}
else{
ans+=w3;
ab.pop();
ab.push(item[i].fi-item[i].se);
}
}
cout<<ans;
return 0;
}