【CF1344D】Résumé Review【数学】【二分】

本文解析了一道算法竞赛题目,该题目要求在给定条件下构造序列以最大化特定数学表达式的值。通过分析函数特性,采用二分查找和优先队列策略,实现了O(nlog²V)的时间复杂度解决方案。

题意:给定长度为nnn的自然数序列aaa和整数kkk,要求构造长度为nnn的自然数序列bbb,使得0≤bi≤ai,∑bi=k0\leq b_i\leq a_i,\sum b_i=k0biai,bi=k,且∑bi(ai−bi2)\sum b_i(a_i-b_i^2)bi(aibi2)最大化。

n≤105n\leq 10^5n105

要求最大化的是b(a−b2)b(a-b^2)b(ab2),记为f(x)=x(a−x2)f(x)=x(a-x^2)f(x)=x(ax2)

注意到xxx每增加111f(x)f(x)f(x)会增加Δi(x)=ai−3x2−3x−1\Delta_i(x)= a_i-3x^2-3x-1Δi(x)=ai3x23x1

注意到对于每个iii,当x≥0x\geq 0x0时,Δi(x)\Delta_i(x)Δi(x)单调递减,题目所求就是所有数中的前kkk大之和,而每个Δi\Delta_iΔi被取的一定是前面若干个,所以可以套路性地维护当前的xix_ixi和一个堆,每次取堆顶让对应的xi+1x_i+1xi+1

这样复杂度是O(klog⁡n)O(k\log n)O(klogn)无法通过

但是每次加上的Δi(x)\Delta_i(x)Δi(x)是单调递减的(废话),所以可以二分最小的d=Δi(x)d=\Delta_i(x)d=Δi(x),再二分出每个Δi\Delta_iΔi被选了多少个,判断∑xi<k\sum x_i<kxi<k

最后∑xi\sum x_ixi可能小于kkk,但每个位置最多只能+1+1+1,直接在当前基础上暴力选k−∑xik-\sum x_ikxi

复杂度O(nlog⁡2V)O(n\log^2V)O(nlog2V)

#include <iostream>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAXN 100005
using namespace std;
typedef long long ll;
const ll INF=4e18;
int a[MAXN],b[MAXN],x[MAXN],p[MAXN],n;
ll k,sum;
inline ll delta(const int& a,const int& x){return x==a? -INF:a-3ll*x*x-3ll*x-1;}
inline bool cmp(int A,int B){return delta(a[A],x[A])>delta(a[B],x[B]);}
inline bool check(ll v)
{
	for (int i=1;i<=n;i++)
	{
		if (delta(a[i],0)<v) x[i]=0;
		else
		{
			int l=0,r=a[i]-1,mid;
			while (l<r)
			{
				mid=(l+r+1)>>1;
				if (delta(a[i],mid)<v) r=mid-1;
				else l=mid;
			}
			x[i]=l+1;
		}
	}
	sum=0;
	for (int i=1;i<=n;i++) sum+=x[i];
	if (sum>k) return false;
	for (int i=1;i<=n;i++) b[i]=x[i];
	return true;
}
int main()
{
	cin>>n>>k;
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	ll l=-INF,r=INF,mid,ans;
	while (l<r)
	{
		mid=(l+r)/2;
		if (check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	check(ans);
	for (int i=1;i<=n;i++) p[i]=i;
	sort(p+1,p+n+1,cmp);
	for (int i=1;i<=k-sum;i++) ++b[p[i]];
	for (int i=1;i<=n;i++) printf("%d ",b[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值