51nod 1457:小K vs. 竹子

题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注

小K的花园种着n颗竹子(竹子是一种茎部中空并且长得又高又快的热带植物)。此时,花园中第i颗竹子的高度是hi米,并且在每天结束的时候它生长ai米。

实际上,小K十分讨厌这些竹子。他曾经试图去砍光它们,但由于竹子的茎部太坚固而失败了,然而,小K制作了魔法锤使这些竹子只能在地面上生长。

由于魔法力量有限,他每天最多能使用K次魔法锤。每次他使用魔法锤敲击竹子,竹子的高度就会减少p米。通过这次改变,如果竹子的高度变为负数,那它转而会变为0米(但它不会消失)。换言之,如果一颗被魔法锤敲击的竹子的高度是h,那它的新高度将会是max(0, h - p)米。我们可以在一天中多次敲击同一颗竹子。

小K将会从今天开始和这些竹子抗战m天。他的目标是在m天后使其中最高的竹子的高度最小化(即,“小K敲击竹子,然后竹子生长”的m次迭代)。找出m天后,最高的竹子的高度的最小可能值。


Input
输入的第一行包含四个以空格隔开的整数n,m和p(1 ≤ n ≤ 10^5 ,1 ≤ m ≤ 5000, 1 ≤k≤ 10, 1 ≤ p ≤ 10^9)。它们分别表示小K 花园中的竹子数,小K抗战的持续天数,每天小K能敲击竹子的最多次数和魔法锤的力量。
接下来n行描述了竹子的特性,第i行(1 ≤ i ≤ n) 包含两个以空格隔开的整数hi和ai,(0 ≤ hi ≤ 10^9, 1 ≤ ai ≤ 10^9),分别表示第i颗竹子的初始高度和生长速率。
Output
输出m天后,最高的竹子的高度的最小可能值。
Input示例
3 1 2 5
10 10
10 10
15 2
Output示例
17

这个题对着codeforces上的题解一直看不太懂。。。主要就是这一块:

ll target = (grow[i] - x) % p ? (grow[i] - x) % p : p;
while (target <= grow[i] - x)
{
	if (target <= h[i])
	{
		t[0]++;
	}
	else if((ll)ceil((double)(target - h[i]) / (double)v[i]) > (m - 1))
	{
		return 0;
	}
	else
	{
		t[(ll)ceil((double)(target - h[i]) / (double)v[i])]++;
	}
	target += p;
}
想了一下午这块的代码应该怎么搞,发现就是求x*v[i]+h[i]>=target target是要砍掉的长度,我们希望只要 x*v[i]+h[i]>=target 刚刚满足的那一天x,就把竹子砍掉。简单来说,就是这个魔法在满足条件的情况下尽量提前使用。搞懂了这块,感觉这个题二分顺畅了好多~。

代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#pragma warning(disable:4996)  
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;

#define INF 0x3fffffff
typedef long long ll;

const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

ll n, m, k, p, le, mid, ri;
ll h[maxn], v[maxn], grow[maxn], t[5005];

void input()
{
	int i;
	scanf("%I64d%I64d%I64d%I64d", &n, &m, &k, &p);

	ri = 0;
	for (i = 1; i <= n; i++)
	{
		scanf("%I64d%I64d", &h[i], &v[i]);
		grow[i] = h[i] + v[i] * m;
		ri = max(ri, grow[i]);
	}
}

bool check(ll x)
{
	ll i, num = 0;
	for (i = 1; i <= n; i++)
	{
		num += max(0LL, (ll)ceil((double)(grow[i] - x) / (double)p));
	}
	if (num > m*k)
	{
		return 0;
	}
	memset(t, 0, sizeof(t));
	for (i = 1; i <= n; i++)
	{
		if (grow[i] <= x)continue;
		ll target = (grow[i] - x) % p ? (grow[i] - x) % p : p;
		while (target <= grow[i] - x)
		{
			if (target <= h[i])
			{
				t[0]++;
			}
			else if((ll)ceil((double)(target - h[i]) / (double)v[i]) > (m - 1))
			{
				return 0;
			}
			else
			{
				t[(ll)ceil((double)(target - h[i]) / (double)v[i])]++;
			}
			target += p;
		}
	}
	ll rest = 0;
	for (i = 0; i < m; i++)
	{
		rest += t[i];
		rest = max(0LL, rest - k);
	}
	return rest == 0;
}

void solve()
{
	le = 0; ri++;

	while (le < ri)
	{
		mid = (le + ri) / 2;
		if (check(mid))
		{
			ri = mid;
		}
		else
		{
			le = mid + 1;
		}
	}
	printf("%I64d", ri);
}

int main()
{
	//freopen("i.txt","r",stdin);
	//freopen("o.txt","w",stdout);

	input();
	solve();

	//system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值