您的位置:首页 > 其它

Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake

2016-02-27 17:07 357 查看
题意:有几个蛋糕,要求把他们按照这样的方式堆起来:

下面的编号比上面的小;下面的体积比上面的小。

求把这些蛋糕堆起来能得到的最大体积是多少。

思路:

首先,一定要排序。按照编号排还是按照体积排?

题目中输入顺序就是按照编号排序的。先考虑按照编号排序。对于每个i,要找到j小于i,且V(j) < V(i)中总体积最大的j,然后到i的最优解就是j的最优解加上V(i)。

初步看上去,好像是dp。

如果每次找j用扫描的办法,可以解决问题,但是复杂度是n方的。

如果每次不用扫描,用线段树来保存从1到i的最大值,这样复杂度世够了,但是无法保证V(j) < V(i)。

按照编号排序貌似不行,但是感觉就差一点点。

换成按照体积排序。这样,我们每次选择到蛋糕一定能满足V(j) < V(i),而且用线段树很容易求出1到i中的最优解,这样两个条件就都能满足了。

因为题目要求V(j)严格小于V(i),所以排序的时候,对于相等的体积,序号小的要放后面。

官方给出题解的译文:

首先,我们计算每个蛋糕的体积: vi=π∗hi∗r2i

现在,考虑序列 v1,v2,v3,…,vn :问题的答案是这个序列中递增子序列的最大和。我们怎么样解决它?首先去掉小数,我们可以定义一个新的数组 a1,a2,a3,…,an,ai=vi/π=hi∗r2i

我们考虑 dpi 是以 ai 结束的序列和的最大值且

dpi=max(ai,maxj<i,aj≤aidp[j]+ai)

这个问题的答案就是: π∗maxi=1tondp[i]

现在,我们怎么计算 dpi=max(ai,maxj<i,aj≤aidp[j]+ai) ?我们使用一个线段树,这个线段树有两种操作:1.将第i个数更改为v;2.找出1到i中最大的数。

现在,我们将dp与线段树结合寻找答案。

假设 a1,a2,a3,…,an 已经排序好了。我们定义 bi是ai 的位置。现在填充 dpi ,我们找出区间 [1,bi] 中最大的, 设为x,然后将线段树中 bi 个位置设置成 ai+x 。问题的答案就是区间[1,n]中的最大值。

时间复杂度: O(nlogn)

代码如下:

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

typedef long long ll;

const double PI = acos(-1);

struct Cake{
ll V;
int id;
bool operator < (const Cake a) const {
if(V != a.V)
return V<a.V;
return id>a.id;
}
}cake[100005];

ll dp[400005];

int p;ll v;
void update(int l,int r,int o){
if(l == r){
dp[o] = v;return;
}
int m = (l+r)/2;
if(p<=m)update(l,m,o*2);
else update(m+1,r,o*2+1);
dp[o] = max(dp[o*2],dp[o*2+1]);
}

int ql,qr;
ll query(int l,int r,int o){
if(l>=ql && r <= qr)return dp[o];
ll ret = 0;
int mid = (l+r)/2;
if(ql <= mid) ret = max(ret,query(l,mid,o*2));
if(qr > mid) ret = max(ret,query(mid+1,r,o*2+1));
return ret;
}

int main()
{
//    freopen("data.txt","r",stdin);
memset(dp,0,sizeof(dp));
int n;
scanf("%d",&n);
for(int i = 1; i<= n; ++i){
ll r,h;
scanf("%lld%lld",&r,&h);
cake[i].V =  r * r * h;
cake[i].id = i;
}
sort(cake+1,cake+n+1);
for(int i = 1; i <= n ; ++i ){
ql = 1;qr = cake[i].id;
v = query(1,n,1) + cake[i].V;
p = cake[i].id;
update(1,n,1);
}
ql = 1;qr = n;
ll ans = query(1,n,1) ;
printf("%.6lf\n",  ans * PI);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: