题目链接:
http://codeforces.com/contest/629/problem/D
题目大意:
给n个圆柱体蛋糕,现在要堆一个大蛋糕,要求体积大的放在上面同时编号大的不能放在编号小的下面。问最大的体积是多少。
范围;
n<=100000.
思路:
很容易想到:dp[i]=dp[j]+a[i](i>j&&a[i]>a[j])。
所以就有了O(n^2)的算法,但是范围太大,会超时。
可以注意到,计算dp[i]的最大值,就是在dp[1]~dp[i-1]的范围里面寻找一个最大的体积。所以我们可以想到利用线段树区间寻找最大值,为了满足a[i]>a[j]这个条件,我们可以先将体积进行排序,然后作为线段树下标。为了表示这个下标,我们要先将体积进行离散化。然后每次寻找区间最大值,更新dp[i],将其放入线段树。这样答案就可以得到了。
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define PI acos(-1.0)
#define M 100005
#define ll __int64
using namespace std;
int num[M];
ll vol[M],f[M];
double dp[M];
struct tree{
int l,r;
double ans;
}tree[M<<2];
void pushup(int i)
{
if(tree[i].l==tree[i].r)return;
tree[i].ans=max(tree[i<<1].ans,tree[i<<1|1].ans);
}
void build(int l,int r,int i)
{
tree[i].l=l;
tree[i].r=r;
if(l==r){
tree[i].ans=0;
return;
}
int mid=l+r>>1;
build(l,mid,i<<1);
build(mid+1,r,i<<1|1);
pushup(i);
}
double query(int l,int r,int i)
{
if(tree[i].l==l&&r==tree[i].r)
{
return tree[i].ans;
}
int mid=tree[i].l+tree[i].r>>1;
if(r<=mid)return query(l,r,i<<1);
else if(l>mid)return query(l,r,i<<1|1);
else return max(query(l,mid,i<<1),query(mid+1,r,i<<1|1));
pushup(i);
}
void update(int l,int r,int i,double z)
{
if(tree[i].l==l&&tree[i].r==r)
{
tree[i].ans=max(tree[i].ans,z);
return;
}
int mid=tree[i].l+tree[i].r>>1;
if(r<=mid)update(l,r,i<<1,z);
else if(l>mid)update(l,r,i<<1|1,z);
else {
update(l,mid,i<<1,z);
update(mid+1,r,i<<1|1,z);
}
pushup(i);
}
int main()
{
int n,i,j,k;
ll r[M],h[M];
while(~(scanf("%d",&n)))
{
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
scanf("%I64d%I64d",&r[i],&h[i]);
vol[i]=r[i]*r[i]*h[i];
f[i]=vol[i];
}
sort(vol+1,vol+1+n);
for(i=1;i<=n;i++)
num[i]=lower_bound(vol+1,vol+1+n,f[i])-vol;
build(1,n,1);
for(i=1;i<=n;i++)
{
if(num[i]-1==0)dp[i]=f[i];
else dp[i]=query(1,num[i]-1,1)+f[i];
update(num[i],num[i],1,dp[i]);
}
double maxi=0;
for(i=1;i<=n;i++)
{maxi=max(maxi,dp[i]);
// printf("%.2f ",dp[i]);
}
printf("%.8f\n",maxi*PI);
}
}