CF506 C Mr. Kitayuta vs. Bamboos (贪心)

题意

有n根竹子,初始高度是h[i],每天结束时会长高a[i],每天你可以砍K刀,一刀能减小p的高度。可以在某一天内砍相同的竹子多次。问m天结束后,最高的竹子最矮是多高。
n 1 e 5 , k 10 , m 5000 n\leq 1e5, k\leq 10,m\leq 5000

思路

  • 题解第一种做法没看懂,写一下第二种
  • 首先二分答案,判定是否存在方案:
  • 考虑倒着做,问题变成了每天缩水一定高度,然后可以做k次长高操作。
  • 中途不能有负高度,并且最后每根竹子的高度不能比一开始矮。
  • 正确性的话,假如存在顺着做的方案让最终每根竹子高度比x小,那将这种方案最终竹子的所有高度调整至x,然后再倒过来,也是一种合法的倒着做方案。
  • 另一方面,只要找到一种倒着做方案,也能反过来变成顺着做的方案。
  • 因此,只需要判断是否存在倒着做的合法方案即可。
  • 因为高度不会溢出,因此所有竹子加的次数都是一定的。只需要安排顺序使得不存在负数。
  • 倒着决定每一刀放哪里,首先操作最快变成负数的竹子。直到所有竹子都在第0天为正后,再考虑让每一根竹子比h[i]大。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n,m,k,p;
ll h[N], a[N], mx;

set<pair<ll,ll> > s;
ll now[N];

bool ok(ll x) {
	s.clear();
	for(int i = 1; i <= n; i++) {
		now[i] = x - a[i];
		s.insert(make_pair(m - (now[i] + a[i]) / a[i], i));
	}

	int can = 1;
	for(int i = m; i; i--) {
		if (s.rbegin() -> first >= i) return 0;
		for(int j = 1; j <= k; j++) {
			if (s.rbegin() -> first > 0) {
				int u = s.rbegin() -> second;
				s.erase(--s.end());
				now[u] += p;
				s.insert(make_pair(i - (now[u] - (m - i) * a[u] + a[u]) / a[u], u));
			} else {
				while(can <= n && now[can] - (m - i) * a[can] - a[can] * (i - 1) >= h[can]) {
					can++;
				}
				if (can <= n) {
					now[can] += p;
				}
			}
		}
	}
	for(int i = 1; i <= n; i++) 
		if (now[i] - (m - 1) * a[i] < h[i]) return 0;
	return 1;
}

int main() {
	freopen("c.in", "r", stdin);
	cin>>n>>m>>k>>p;
	for(int i = 1; i <= n; i++) {
		scanf("%I64d %I64d",&h[i], &a[i]);
		mx = max(mx, a[i]);
	}
	ll L = mx, R = 1e9 * m + 1e9, ans = 0;
	while (L <= R) {
		if (ok(L + R >> 1)) {
			R = ans = L + R >> 1;
			R--;
		} else L = (L + R >> 1) + 1;
	}
	cout << ans << endl;
}
发布了266 篇原创文章 · 获赞 93 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/102826440