[CF629D] Babaei and Birthday Cake

[CF629D] Babaei and Birthday Cake - dp,树状数组

Description

有n个圆柱形的蛋糕,已知它们的底面半径r和侧面高h。把这n个蛋糕叠起来,第i个蛋糕放在第j个蛋糕上面(1<=j<=i-1)的条件是第i个蛋糕的体积必须大于第j个蛋糕的体积。求叠放起来的蛋糕体积最大能是多少?

Solution

这里用传统的 LIS 二分答案方法维护的问题在于下标过大,因此有两种方法

一种是用 map 维护,思路其实一样,大体代码类似这样

auto l=mp.lower_bound(volume);
int ans=(--l)->second+volume;
auto r=++l;
while(r!=mp.end() && r->second<=ans) ++r;
mp.erase(l,r);
mp[volume]=ans;

另一种是绕开二分答案,按体积升序(相同则编号降序)排序后对编号做 LIS,这样可以用树状数组维护

相同则编号降序是重要的,可以防止重复转移 (wa 37)

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5 + 5;

// todo: binary index tree (max)        2'

int bt[N];

int lowbit(int x)
{
    return x & (-x);
}

void add(int i, int x)
{
    while (i < N)
        bt[i] = max(bt[i], x), i += lowbit(i);
}

int query(int i)
{
    int ans = 0;
    while (i > 0)
        ans = max(ans, bt[i]), i -= lowbit(i);
    return ans;
}

// todo: dp main                        3'

int f[N], n;

struct item
{
    int r, h, v, id;
    bool operator<(const item &rhs) const
    {
        if (v != rhs.v)
            return v < rhs.v;
        else
            return id > rhs.id; // wa 37
    }
} a[N];

signed main()
{
    ios::sync_with_stdio(false);

    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].r >> a[i].h;
        a[i].v = a[i].r * a[i].r * a[i].h;
        a[i].id = i;
    }
    sort(a + 1, a + n + 1);

    for (int i = 1; i <= n; i++)
    {
        int id = a[i].id, v = a[i].v;
        f[i] = query(id) + v;
        add(id, f[i]);
    }

    cout << fixed << setprecision(12) << *max_element(f + 1, f + n + 1) * 3.14159265358979 << endl;
}

原文地址:https://www.cnblogs.com/mollnn/p/14618528.html