CF505E Mr. Kitayuta vs. Bamboos

看到陈指导写的标签就直接知道怎么做了,想到倒着做还是比较简单的

首先最大值最小我们一眼二分答案(x),然后我们就发现那个(max(h_i-p,0))很难处理

考虑倒着做,这样操作就变成了:

  • 初始时每个数都是(x)
  • (k)次,每次选择一个数加上(p)
  • 将所有数减去(a_i)

最后要求做的过程中所有数都要大于等于(0)并且最后要超过(h_i)

思考如何检验,我们发现此时我们可以把增加的操作攒起来一起加

容易想到我们可以开一个,分别记录做(i)次操作后哪些位置会小于(0)

枚举减的操作次数,将对应的桶内的元素需要的加上次数累加后重新计算扔进桶里即可

由于加数操作在第(i)次不能超过(i imes k),因此检验一次复杂度就是(O(n+mk))

综上,总复杂度(O((n+mk)log (p imes m)))

#include<cstdio>
#include<vector> 
#define RI register int
#define CI const int&
#define int long long
using namespace std;
typedef vector <int>:: iterator VI;
const int N=100005,M=5005;
int n,m,k,p,h[N],a[N],s[N],ans; vector <int> v[M];
inline void expand(CI x,CI y)
{
	if (x<m) v[x].push_back(y);
}
inline bool check(CI x)
{
	RI i; int sum=0; for (i=1;i<=m;++i) v[i].clear();
	for (i=1;i<=n;++i) expand((s[i]=x)/a[i],i);
	for (i=0;i<m;++i) for (VI it=v[i].begin();it!=v[i].end();++it)
	{
		int dlt=(a[*it]*(i+1)-s[*it]+p-1)/p;
		if (s[*it]+=dlt*p,(sum+=dlt)>i*k) return 0; expand(s[*it]/a[*it],*it);
	}
	for (i=1;i<=n;++i) if (s[i]<a[i]*m+h[i])
	if ((sum+=(a[i]*m+h[i]-s[i]+p-1)/p)>m*k) return 0; return 1;
}
signed main()
{
	RI i; for (scanf("%lld%lld%lld%lld",&n,&m,&k,&p),i=1;i<=n;++i)
	scanf("%lld%lld",&h[i],&a[i]); int l=1,r=1e15,mid; while (l<=r)
	if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
	return printf("%lld",ans),0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/14084009.html