首页 > 技术文章 > CF505E Mr. Kitayuta vs. Bamboos

smyjr 2019-11-02 17:27 原文

cf

luogu

要使得最高的柱子高度最小,考虑二分这个高度,那么剩下的就是要指定一个操作方案,使得最终每个柱子高度\(\le mid\)

因为有个柱子高度不会\(<0\)的限制,所以正着模拟不太方便维护.考虑倒着模拟,那么问题可以转化成一开始有\(n\)个高度为\(mid\)的柱子,有\(m\)天,每天每个柱子先会减少\(a_i\)高度(还要保证柱子高度每个时刻\(\ge 0\)),然后每个可以操作\(k\)次,每次选一个柱子使得其高度增加\(p\),要使得最终每个柱子高度\(\ge h_i\)

现在要知道每次操作应该选哪个柱子操作.因为要保证柱子高度每个时刻\(\ge 0\),所以应该优先选择高度最快会\(<0\)的柱子操作,形式化的讲就是如果\(t\)时刻有柱子高度为\(he\),如果中途不操作,那么这个柱子在\(t+\lfloor\frac{he}{a_i}\rfloor+1\)时刻会\(<0\),所以要在这个时刻前增加他的高度.然后显然是选择高度马上就要\(<0\)的操作最优.注意过程中已经有高度\(<0\)就不合法,如果有往后一直不操作,最终高度可以\(\ge h_i\)的柱子就不用管了.这个可以用堆维护,最后看堆是否为空即可

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1e5+10,M=5000+10;
LL rd()
{
	LL x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
int n,m,kk;
LL h[N],a[N],cv3;
struct node
{
	int x,t;
	LL h,d;
	bool operator < (const node &bb) const {return d>bb.d;}
};
priority_queue<node> hp;

int main()
{
    ///cfzmhs
	n=rd(),m=rd(),kk=rd(),cv3=rd();
	LL l=0,r=(LL)1e9*(m+1);
	for(int i=1;i<=n;++i)
	{
		h[i]=rd(),a[i]=rd();
		l=max(l,a[i]);
	}
	while(l<=r)
	{
		LL mid=(l+r)>>1;
		while(!hp.empty()) hp.pop();
		for(int i=1;i<=n;++i)
			if(mid-a[i]*m<h[i])
				hp.push((node){i,1,mid-a[i],mid/a[i]});
		bool ok=1;
		for(int i=1;ok&&i<=m;++i)
		{
			int rs=kk;
			while(!hp.empty()&&rs)
			{
				node nw=hp.top();
				hp.pop();
				int x=nw.x;
				if(nw.h-a[x]*(m-nw.t)>=h[x]) continue;
				--rs;
				nw.h-=a[x]*(i-nw.t);
				if(nw.h<0) {ok=0;break;}
				nw.h+=cv3;
				hp.push((node){x,i,nw.h,i+nw.h/a[x]});
			}
			if(hp.empty()) break;
		}
		while(!hp.empty()&&hp.top().h-a[hp.top().x]*(m-hp.top().t)>=h[hp.top().x]) hp.pop();
		ok&=hp.empty();
		if(ok) r=mid-1;
		else l=mid+1;
	}
	printf("%lld\n",r+1);
	return 0;
}

推荐阅读