【wqs 二分】【反悔贪心】P4694 [PA2013] Raper

手动模拟一下,发现 (i,g(i))(i,g(i)) 构成一个下凸包。

可以进行 wqs 二分。b=kx+yb = -kx + y,把 bb 看作将每个物品的价值都减去 kk 后,选任意个物品的最小代价,此时商品代价可能是负数,因为 b 工厂的代价全部是正数,所以我们只需要考虑 ai<0a_i < 0 的情况。

说白了,现在我们要构造两个长度为 kk 的序列 p,qp,q,使它们满足对于任意的 ii,有 piqip_i \leq q_i

可以用小根堆来计算最小的 bib_i,每次选取 aia_i 的时候,就取出来一个最小的 bib_i,也就是最优决策,比较 w=ai+bmin<0w=a_i + b_{min} < 0。本步骤需要倒序枚举 ii,因为要满足 piqip_i \leq q_i

此时我们漏了一种情况。

例如:a={100,2,10},b={101,1,10000}a = \{-100,-2,10\},b = \{101,1,10000\}。按照上述策略,就会选择 2,1-2,1。考虑将以前的决策替换掉,也就是说不用重新算 b 工厂的代价,减去原决策的代价,再加上当前的代价。如何实现呢?可以把替换的决策加入堆中,也就是加入一种决策: ai-a_i。这就是反悔贪心啦。


/*
	- 别摆了,哥
	- 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;
}
posted @ 2024-06-05 23:05  cjrqwq  阅读(11)  评论(0)    收藏  举报  来源