CF2055E Haystacks

n 个干草堆,标记从 1n,其中干草堆 i 包含 ai 捆干草。一个干草堆下面隐藏着一根针,但你不知道是哪一个。你的任务是移动干草捆,以确保每个干草堆至少被清空一次,从而检查针是否隐藏在那个特定的干草堆下。然而,这个过程并不简单。一旦干草堆 i 第一次被清空,它将被分配一个高度限制,不能再包含超过 bi 捆干草。

更正式地说,移动的描述如下:

选择两个干草堆 ij。如果干草堆 i 之前没有被清空,或者干草堆 i 包含的干草捆严格少于 bi,你可以将正好 1 捆干草从干草堆 j 移动到干草堆 i

注意:在干草堆被清空之前,它没有高度限制,你可以将任意数量的干草捆移动到该干草堆上。

计算确保每个干草堆至少被清空一次所需的最小移动次数,或者报告这不可能。


草堆题。

ci=aij=1i1bjaj

首先我们可以考虑先随便钦定一个操作排列,然后从前往后,清空每一个稻草堆 i

假如 ci0,那么 ai 就可以放进前面的空位里,消耗 ai 次操作。

假如 ci>0,那么前面的空位不足以容纳 ai,我们此时需要找到后面的一堆存放 ci

我们贪心地,会发现把放不下的部分放到最后一堆一定是最优的。

特殊地,考虑最后一堆,最后一堆里面最后会有 ai+maxi=1nci 个稻草,只要前面的位置放得下,我们就可以将他们全部移走来清空最后一堆。

不难发现,答案就是 ai+maxi=1nciai 是常数,所以我们只要考虑最小化 maxi=1nci 即可。

首先把满足 aibi 的放在 ai>bi 的前面肯定是不劣的。

然后我们考虑两个位置 x<y,我们会交换他们两个当且仅当 max(ax,axbx+ay)>max(ay,ayby+ax),拆开得到:

(ax>ayax>ayby+ax)(axbx+ay>ayaxbx+ay>ayby+ax)

化简得到:

(ax>ayayby<0)(axbx>0bx<by)

由此可以推出,当 axbx<0 时,我们应按 ax 降序排序,当 axbx>0 时,我们应按 bx 升序排序,此时是最优的。

但是直接排序然后暴力并不是对的,因为最后一个数是需要特殊处理的。

因此我们需要枚举最后一位,然后计算最小的答案。

有一种可行的实现方法是,先把序列排序,然后从前往后扫,同时模拟清空的过程,同时在第 i 位把 i1 的前缀的结果放进数据结构里,然后在数据结构里进行模拟,这样最后数据结构里会维护 n 个结果,每个都被挖了一个空,我们枚举空计算结果即可,以下是一份可能的实现:

#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
using  std::cin, std::cout;
const int N = 5e5 + 7;
#define int long long
int n, h[N], c[N], d = 0, f = 0, e, g[N];
std::set<std::pair<int, int>> s;
struct o {int a, b;} v[N];
int find(int x) {
	static int s[N]; int t = 0;
	while(h[x] != x) s[++t] = x, x = h[x];
	for(int i = t - 1; i >= 1; --i)
		c[s[i]] += c[s[i+1]], h[s[i]] = x;
	return x;
}
bool cmp(o p1,o p2){
	static auto f = [](o x) {return x.a < x.b ? -1 : x.a > x.b ? 1 : 0;};
	return f(p1)!=f(p2) ? f(p1)<f(p2) : f(p1)<=0 ? p1.a<p2.a : p1.b>p2.b;
}
inline void solve() {
	f = d = 0, e = 1e18; cin >> n; 
	for(int i = 1; i <= n; ++i) cin >> v[i].a >> v[i].b, f += v[i].a;
	for(int i = 1; i <= n; ++i) h[i] = i, ::c[i] = 0; 
	std::sort(v + 1, v + n + 1, cmp);
	for(int i = 1, sig = 0, c = 0; i <= n; ++i) {
		{	auto [a, b] = v[i - 1];
			if(a <= sig) sig -= a;
			else c += a - sig, sig = 0;
			sig += b;
		}
		auto [a, b] = v[i]; d += a;
		if(auto t = s.upper_bound({d, n}); t != s.begin()) {
			int j = (--t)->second; ::c[j] += d - t->first;
			for(auto p = s.begin(); p != t; ++p)
				h[p->second] = j, ::c[p->second] += (d - p->first) - ::c[j];
			s.erase(s.begin(), ++t), s.insert({d, j});
		}
		d -= b;
		::c[i] = c, s.insert({sig + d, i});
	}
	for(auto& [x, y]: s)
		g[y] = x - d;
	s.clear();
	for(int i = 1; i <= n; ++i) { 	
		int j = find(i), c = ::c[i] + (j == i ? 0 : ::c[j]);
		if(v[i].a + c <= g[j]) e = std::min(e, f + c);
	}
	cout << (e == 1e18 ? -1 : e) << "\n";
}
signed main() {
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int t; cin >> t; for(; t--; ) solve();
}
posted @   CuteNess  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示