http://codeforces.com/problemset/problem/629/D
有n个蛋糕,从1编号到n,现在用这些蛋糕制作一个大蛋糕,要求编号大的蛋糕必须放在编号小的蛋糕上面,且上面的蛋糕的体积必须严格大于下面蛋糕的体积,问能制作出的大蛋糕的最大体积
定义 dp[i]=max(dp[j]|j<i∧vj<vi)+vi ,如何快速求出这个最大的 dp[j] 是关键,可以用线段树来维护,线段树上的节点保存的是以某个体积的蛋糕为最上层蛋糕所能制作的最大蛋糕,首先按照序号从小到大来遍历所有的蛋糕,离散化体积,查询到 i 时, i 之前的点都已经更新到线段树上了,查询 [1,id[i]−1] 中的最大值,这样保证编号是小于 i 的,且单个体积也是严格小于 i 的,然后把 dp[i] 更新上去,为以后的查询做准备
#include
using namespace std;
const int N = 100100;
const double PI = acos(-1), eps = 1e-8;
typedef pair<double, double> P;
struct node
{
int l, r;
double val;
}g[N*4];
int r[N], h[N], id[N];
double v[N], vv[N], dp[N];
void push_up(int k)
{
g[k].val = max(g[k<<1].val, g[k<<1|1].val);
}
void build(int l, int r, int k)
{
g[k].l = l, g[k].r = r, g[k].val = 0.0;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, k << 1);
build(mid+1, r, k << 1|1);
push_up(k);
}
void update(int l, int r, double val, int k)
{
if(g[k].l == l && g[k].r == r)
{
g[k].val = val;
return;
}
int mid = (g[k].l + g[k].r) >> 1;
if(l <= mid) update(l, r, val, k << 1);
if(r > mid) update(l, r, val, k <<1|1);
push_up(k);
}
double query(int l, int r, int k)
{
if(l <= g[k].l && g[k].r <= r) return g[k].val;
int mid = (g[k].l + g[k].r) >> 1;
double res = 0.0;
if(l <= mid) res = max(res, query(l, r, k << 1));
if(r > mid) res = max(res, query(l, r, k << 1|1));
return res;
}
int main()
{
int n;
while(~ scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &r[i], &h[i]);
v[i] = vv[i] = PI * r[i] * r[i] * h[i];
}
sort(vv+1, vv+1+n);
for(int i = 1; i <= n; i++)
{
id[i] = lower_bound(vv+1, vv+1+n, v[i]) - vv;
}
build(1, n, 1);
for(int i = 1; i <= n; i++)
{
if(id[i]-1 == 0) dp[i] = v[i];
else dp[i] = query(1, id[i]-1, 1) + v[i];
update(id[i], id[i], dp[i], 1);
}
double res = 0.0;
for(int i = 1; i <= n; i++) res = max(res, dp[i]);
printf("%.9f\n", res);
}
return 0;
}