题意
有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;
}