題目鏈接:
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);
}
}