CF505E Mr. Kitayuta vs. Bamboos

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

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

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

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

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

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

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

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

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

综上,总复杂度\(O((n+mk)\log (p\times 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;
}
posted @ 2020-12-04 09:15  空気力学の詩  阅读(112)  评论(0编辑  收藏  举报