【wqs 二分】【反悔贪心】P4694 [PA2013] Raper
手动模拟一下,发现 构成一个下凸包。
可以进行 wqs 二分。,把 看作将每个物品的价值都减去 后,选任意个物品的最小代价,此时商品代价可能是负数,因为 b 工厂的代价全部是正数,所以我们只需要考虑 的情况。
说白了,现在我们要构造两个长度为 的序列 ,使它们满足对于任意的 ,有 。
可以用小根堆来计算最小的 ,每次选取 的时候,就取出来一个最小的 ,也就是最优决策,比较 。本步骤需要倒序枚举 ,因为要满足 。
此时我们漏了一种情况。
例如:。按照上述策略,就会选择 。考虑将以前的决策替换掉,也就是说不用重新算 b 工厂的代价,减去原决策的代价,再加上当前的代价。如何实现呢?可以把替换的决策加入堆中,也就是加入一种决策: 。这就是反悔贪心啦。
/*
- 别摆了,哥
- By yfz
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+10;
#define pii pair<int,int>
int n,k;
int a[N],_a[N],b[N];
pii gd(int x) {
for(int i=1;i<=n;i++) a[i] = _a[i] - x;
priority_queue<pii,vector<pii>,greater<pii>>q;
/*
- {x,y} 的意义:
x 是更新的代价,y 是增加序列长度的数量
当 y = 0 时,说明是替换,此时 now_w 为 a_i - a_j。
当 y = 1 时,说明新增一个, 此时 now_w 为 a_i + b_i。
*/
int cnt = 0, ans = 0;
for(int i = n;i > 0;i--) {
q.push({b[i],1});
if(a[i] >= 0) continue;
pii _small = q.top(); int now_w = a[i] + _small.first;
if(now_w < 0 || (now_w == 0 && _small.second <= 0)) {
// || 右边的式子也可以不用写。 因为替换和不替换效果完全相同。
// 但是作者想说的是,判断共线这个细节还是要注意的
ans += now_w, cnt += _small.second, q.pop(), q.push({-a[i],0});
}
}
return {cnt,ans};
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>_a[i];
for(int i=1;i<=n;i++) cin>>b[i];
int L = 0, R = 2e9+5;
while(L + 1 < R) {
int mid = L+R >> 1; pii o = gd(mid);
if(o.first <= k) L = mid;
else R = mid;
}
pii o = gd(L);
cout<<o.second + k * L;
return 0;
}

浙公网安备 33010602011771号