[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;
}