您的位置:首页 > 其它

Codeforces Round #356 (Div. 1) B

2016-06-10 00:05 169 查看
题意:

Limak is a little polar bear.它有一个数m(1<=m<=10^15),它把这个数拆成一堆三次方数的和,也就是当前剩余数首先减去小于等于当前剩余数的最大的那个三次方数,直道把这个数减成0.现在Limak想知道,给出一个数m,在[1,m]中找到一个数x,使得x在上述拆分条件下,拆出的三次方数最多,同时满足x尽量大



题解:

先不考虑给出的m,如果有两个区间[1,x1],[1,x2],当x1>x2,前一个区间里面的答案无论是在拆出的三次方数的个数还是答案的大小,都不会比第二个差。然后2^3-1^3,3^3-2^3,4^3-3^3........这样持续下去,每个值是一直增大的。还有,如果我们列出所有3次方数,1,8,27,64,125,216,...显然125以后存在2*n^3>(n+1)^3,因此125以及后面的数,要取,每个数被取到的次数一定不超过1.最后我们注意到(10^5)^3
= 10^15.


假定我们有一个数n,它会被怎么拆?每次二分找到一个最大的小于等于n的三次方数,然后n减去它,这样会得到一个数x,由发现1我们知道,这个x显然越大越好。

如何让x最大?一开始我们有区间[1,m],由发现2知道,n取的越大,x可能越大,这样我们二分最大的a,使得a^3<=m,这样x最大值要么是m
- a^3要么是a^3 - 1 - (a-1)^3。这样又能发现,如果当前选a^3或(a+1)^3的同时,不但让剩余部分趋向于有更好的组成方案,而且当前的组成部分又多了一个不错的大数,显然这样的取法是局部最优的,而本题显然,所有的局部最优决定了全局最优解。


但是该取哪一个?实践(WA)告诉我们,本问题要满足的第一条件,即拆分个数增长是十分缓慢的,这就导致你两边不得不都试一遍,因为两边都可能是局部最优解。

实践又告诉我们,第一个条件是不超过18的,虽然官方题解貌似复杂度更低,但是,爆搜真的很好写~~

上述解法对每次每个三次方数只取一个的情况保证管用,事实上,当a^3 = 27或更小,这样的a同时可以取的不止一个,这时是否管用?苟蒻没验证过,比赛的时候直接预处理1W一下的解,爆搜的时候碰到就直接返回了


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

typedef long long LL;

LL n,ans,p[100010],a1[10010],a2[10010];

struct data{
LL tot,num;
};

data dfs(LL now)
{
data ret;
if (now <= 10000) return (data){a1[now],a2[now]};
int pos = lower_bound(p + 1,p + 100001,now) - p; --pos;
LL x1 = now - p[pos];
LL x2 = p[pos] - p[pos-1];
data r1 = dfs(x1); r1.num += p[pos];
data r2 = dfs(x2); r2.num += p[pos-1];
if (r1.tot > r2.tot) ret = r1;
else if (r2.tot > r1.tot) ret = r2;
else if (r1.num > r2.num) ret = r1;
else ret = r2;
++ret.tot;
return ret;
}

int main()
{
#ifdef YZY
freopen("yzy.txt","r",stdin);
#endif

cin >> n;
for (LL i = 1; i <= 100000; i++) p[i] = i*i*i;
for (int i = 1; i <= 10000; i++) {
int x = i;
for (int j = 100; j; j--)
while (x >= p[j])
x -= p[j],++a1[i];
}
for (int i = 1; i <= 10000; i++) {
a2[i] = i;
if (a1[i-1] > a1[i]) a1[i] = a1[i-1],a2[i] = a2[i-1];
}

data ret = dfs(n);
cout << ret.tot << ' ' << ret.num;
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: