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

35 篇文章 0 订阅
16 篇文章 0 订阅

题意:有几个蛋糕,要求把他们按照这样的方式堆起来:
下面的编号比上面的小;下面的体积比上面的小。
求把这些蛋糕堆起来能得到的最大体积是多少。
思路:
首先,一定要排序。按照编号排还是按照体积排?
题目中输入顺序就是按照编号排序的。先考虑按照编号排序。对于每个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=πhir2i
现在,考虑序列  v1,v2,v3,,vn :问题的答案是这个序列中递增子序列的最大和。我们怎么样解决它?首先去掉小数,我们可以定义一个新的数组  a1,a2,a3,,an,ai=vi/π=hir2i
我们考虑  dpi 是以  ai 结束的序列和的最大值且
 dpi=max(ai,maxj<i,ajaidp[j]+ai)
这个问题的答案就是:  πmaxi=1tondp[i]
现在,我们怎么计算  dpi=max(ai,maxj<i,ajaidp[j]+ai) ?我们使用一个线段树,这个线段树有两种操作:1.将第i个数更改为v;2.找出1到i中最大的数。
现在,我们将dp与线段树结合寻找答案。
假设  a1,a2,a3,,an 已经排序好了。我们定义  biai 的位置。现在填充  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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值