[CF506C]Mr. Kitayuta vs. Bamboos

Description

有n个竹子,第i个竹子长度为h[i],每天的结束会长高a[i]
现在有m天,每一天可以做k次操作,每次操作可以选择一个竹子砍掉p,即高度h[i]=max(h[i]-p,0)
你需要最小化m天结束后最高的竹子的高度
n<=100000,m<=5000,k<=10

Solution

先考虑二分答案ans,然后有两种做法:
Solution 1:
考虑每个竹子,我们至少需要砍t[i]=max(0,h[i]+ma[i]ansp)t[i]=max(0,\lceil {h[i]+m*a[i]-ans\over p}\rceil)
显然我们没有必要砍更多次
若∑t[i]>m*k显然是非法,所以我们可以依次判定每一刀
我们可以求出di,jd_{i,j}表示,若要在第i根竹子砍第j刀,至少需要在第di,jd_{i,j}
考虑把这一刀挂在第di,jd_{i,j}天,从前往后扫每一天,能砍就砍,最后能砍完就合法
Solution 2:
考虑倒过来,所有竹子初始高度为ans,每一天所有竹子会先变矮a[i],然后每次操作选择一个竹子拔高p
限制是操作过程中所有竹子的高度非负
那么每次操作肯定选最快到0的竹子拔高,用一个堆来维护即可
判定的条件是每个竹子的高度>=h[i]

Code

这里是第一种解法:

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=1e5+5;

int n,m,k,p,cnt[N];
ll l[N],h[N],a[N];

bool check(ll x) {
	ll tot=0;
	fo(i,1,n) l[i]=h[i]+a[i]*m,tot+=max(0ll,(l[i]-x+p-1)/p);
	if (tot>m*k) return 0;
	fo(i,0,m) cnt[i]=0;
	fo(i,1,n) 
		if (l[i]>x) 
			for(ll tmp=(l[i]-x-1)%p+1;tmp<=l[i]-x;tmp+=p) 
				if (tmp<=h[i]) cnt[0]++;
				else if (tmp>h[i]+a[i]*(m-1)) return 0;
				else cnt[(tmp-h[i]+a[i]-1)/a[i]]++;
	int ret=0;
	fo(i,0,m-1) {
		ret+=cnt[i];
		ret=max(0,ret-k);
	}
	return !ret;
}

int main() {
	n=read();m=read();k=read();p=read();
	fo(i,1,n) h[i]=read(),a[i]=read();
	ll l=0,r=1e15,ans=0;
	while (l<=r) {
		ll mid=l+r>>1;
		if (check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld\n",ans);
	return 0;
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值