【noi.ac】#171. 立方体

题目

TonyFang打算送你一些立方体。

你需要在[1,n][1,n]中选择一个整数kk。在送你的立方体的体积和不超过kk的情况下,TonyFang会不断给你一个边长为正整数且尽可能大的立方体。

你需要求出最多能得到多少个立方体,以及在此条件下,kk的最小值和最大值。

Input
一行一个整数nn。

Output
三行,每行一个整数,分别表示最多立方体个数,容量最小值和容量最大值。

Examples
14
7
7
14
Notes
对于20%20%的数据,n≤1000n≤1000。

对于40%40%的数据,n≤100000n≤100000。

对于100%100%的数据,1≤n≤10151≤n≤1015。

对于每组数据,第一行正确可以得到30%30%的分数,第二行正确可以得到20%20%的分数,第三行正确可以得到50%50%的分数。请不要输出空行,不会的几行可以随便输出一个数。

思路

显然最后取走的立方体体积和恰好是 k,那么一种取立方体的方
案就对应了一个 k。

一个方案合法当且仅当取走的立方体从小到大排序后满足
x13+ x23+ + xi3 < (xi + 1)3。
对于前两问,从 x1 开始确定每个数。显然 xi 越小是越优的,直
接贪心即可。
对于第三问,考虑两个方案 x, y,如果 xi > yi 并且
xi+1 = yi+1, xi+2 = yi+2, …, xm = ym,那么 x 的体积和显然大于
y。因此,我们从 xm 开始确定每个数,在保证个数的前提下,最
大化当前 xi

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std; 
ll r[100010],e[25],f[25],n; 
int yjy[25],siz; 
int main()
{
	scanf("%lld",&n); 
	for(ll i=1; i<=100000; ++i)
	{
		r[i]=i*i*i; 
	}
	ll ans; 
	ans=0; 
	int cnt=1; 
	while (1)
	{
		while(ans+r[cnt]>=r[cnt+1]&&ans+r[cnt]<=n)
			cnt++; 
		ans+=r[cnt]; 
		siz++; 
		yjy[siz]=cnt; 
		if(ans>n)
		{
			ans-=r[cnt]; 
			siz--; 
			break; 
		}
	}
	for(int i=1; i<=siz; ++i)
		e[i]=e[i-1]+r[yjy[i]]; 
	printf("%d\n",siz); 
	printf("%lld\n",ans); 
	ll as=ans; 
	int o; 
	for(int i=siz; i>=1; --i)
	{
		while(as+r[yjy[i]+1]-r[yjy[i]]<=n)
		{
			o=0; 
			yjy[i]++; 
			for(int j=i; j<=siz; ++j)
			{
				f[j]=e[j]; 
				e[j]+=r[yjy[i]]-r[yjy[i]-1]; 
				if(e[j]>=r[yjy[j]+1])
					o=1; 
			}
			if(o)
			{
				for(int j=i; j<=siz; ++j)
					e[j]=f[j]; 
				yjy[i]--; 
				break; 
			}
			as+=r[yjy[i]]-r[yjy[i]-1]; 
		}
	}
	printf("%lld",as); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值