CF R 639 div2 F Review 贪心 二分

LINK:Résumé Review

这道题让我眼前一亮没想到二分这么绝.

由于每个\(b_i\)都是局部的 全局只有一个限制\(\sum_{i=1}^nb_i=k\)

所以dp没有什么用 我们只需要满足他们的累和=k即可。

容易想到每次给b加1带来的贡献是 \(\Delta_x=a_i-3{b_i}^2-3b_i-1\)

开一个堆每次取出最大值 这样显然是最优的。

不过复杂度为klogn k足足有1e14这么大。

一个绝妙的想法 每次增加的值是递减的 那么第k次增加的值也是固定的。

可以进行二分第k次增加的值 此时我们可以快速算出之前的所有的\(b_i\) 至此从而判定答案是否是最优的。

正确性比较显然 值得一提的是 二分出来之后 我的处理办法是 让每个b都达到临界点 即再增加1就会<=当前mid

这样的话 对于二分出小于答案的那些都会不合法 最后唯一被卡在最左边界的就是答案了。

值得一提的是 求出每个b 可以直接解方程 也可以采用二分 不过解方程之后调整次数我难以把握 所以再接了一个二分.

不过前者复杂度可以认为是nlogn 后者则是nlog^2.

/一个显然的做法 开堆贪心 klogn
//在这个过程中可以发现每次增加的值递减 可以二分出来这个东西.
const ll MAXN=100010;
ll n,k,ww;
ll a[MAXN],b[MAXN];
inline ll f(ll a,ll b){return a==b?-INF:a-3*b*(b+1)-1;}
//二分出来的东西要尽可能的<=x
inline ll calc(ll a,ll x)//询问当递减的值为x时的bi的值.
{
	ll l=0,r=a;
	while(l+1<r)
	{
		ll mid=(l+r)>>1;
		if(f(a,mid)>x)l=mid;
		else r=mid;
	}
	if(f(a,r)>x)return min(r+1,a);
	return r;
}
inline ll check(ll x)//递减的值为x.
{
	ww=0;
	rep(1,n,i)
	{
		b[i]=calc(a[i],x);
		ww+=b[i];
	}
	return ww<k;
}
signed main()
{
	freopen("1.in","r",stdin);
	ll l=0,r=0;
	get(n);get(k);
	rep(1,n,i)get(a[i]),r=max(r,f(a[i],0)),l=min(l,f(a[i],a[i]-1));
	while(l+1<r)
	{
		ll mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid;
	}
	if(check(l))r=l;
	check(r);k-=ww;
	rep(1,n,i)if(k>0&&b[i]<a[i]&&f(a[i],b[i])==r)++b[i],--k;
	rep(1,n,i)put_(b[i]);return 0;
}
posted @ 2020-05-08 11:03  chdy  阅读(145)  评论(0编辑  收藏  举报