codeforces 629 D. Babaei and Birthday Cake dp + 线段树

本文讨论了如何使用动态规划解决将多个圆柱形蛋糕叠放的问题,确保每个蛋糕下面的体积严格小于上面的体积。通过排序、维护标号最大值和计算体积,实现最优叠放方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

有n个圆柱形蛋糕,现在需要把他们叠在一起,要求标号小的蛋糕在下面,并且要求保证下面的蛋糕的体积严格的比上面的小。

思路

类似于“最重子序列”,用dp[i]以第i个蛋糕为最高的能得到的最大的体积。dp[i] = max(dp[j] + a[i].v);,此时j要满足两个条件,j蛋糕的标号要比i小,并且j的体积要比i的体积小。

此时我们可以对蛋糕的体积排序,这样从左往右进行dp的时候便可以保证体积的大小关系了。
而对于另一个标号的条件,我们可以用线段树维护一段标号的最大值。

此时还有一个问题,就是当体积一样的时候,可能会存在错误(两个蛋糕都选了)。解决这个问题,便需要先对标号打的蛋糕进行计算,所以修改一下排序就行了。

精度控制上,先不要计算 PI,先计算其他的整数部分,在最后输出时乘 PI

code

#include <bits/stdc++.h>

using namespace std;

#define debug(a) cout << (#a) << ": " << a << endl

const double PI = acos(-1);

struct Node {
    long long r, h;
    long long v;
    int p;

    void volume () {
        v = 1LL * r * r * h;
    }
} a[100000 + 5];

bool cmp (Node a, Node b) {
    if (a.v != b.v) return a.v < b.v;
    else return a.p > b.p;
}

long long sg[4 * 100000 + 5];

long long query (int l, int r, int lr, int L, int R) {

    if (L <= l && r <= R) {
        return sg[lr];
    }

    int m = (l + r) / 2;

    long long res = 0;

    if (L <= m) res = max(res, query(l, m, lr<<1, L, R));
    if (m <  R) res = max(res, query(m+1, r, lr<<1|1, L, R));

    return res;
}

void modify (int l, int r, int lr, int p, long long s) {
    if (l == r && l == p) {
        sg[lr] = s;
        return ;
    }

    int m = (l + r) / 2;

    if (p <= m) modify (l, m, lr<<1, p, s);
    if (m <  p) modify (m+1, r, lr<<1|1, p, s);

    sg[lr] = max(sg[lr<<1], sg[lr<<1|1]);
}

int n;

int main () {
    scanf ("%d", &n);

    for (int i=1; i<=n; i++) {
        scanf ("%I64d %I64d", &a[i].r, &a[i].h);
        a[i].volume();
        a[i].p = i;
    }

    sort (a+1, a+1+n, cmp);
    memset (sg, 0, sizeof(sg));

    modify (1, n, 1, a[1].p, a[1].v);

    for (int i=2; i<=n; i++) {
        long long tmp = query (1, n, 1, 1, a[i].p);
        modify (1, n, 1, a[i].p, tmp + a[i].v);
    }

    long long urc = query(1, n, 1, 1, n);

    printf ("%.8f\n", urc * PI);

    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值