Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake

1 篇文章 0 订阅
1 篇文章 0 订阅

D. Babaei and Birthday Cake
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

As you know, every birthday party has a cake! This time, Babaei is going to prepare the very special birthday party's cake.

Simple cake is a cylinder of some radius and height. The volume of the simple cake is equal to the volume of corresponding cylinder. Babaei has n simple cakes and he is going to make a special cake placing some cylinders on each other.

However, there are some additional culinary restrictions. The cakes are numbered in such a way that the cake number i can be placed only on the table or on some cake number j where j < i. Moreover, in order to impress friends Babaei will put the cake i on top of the cake j only if the volume of the cake i is strictly greater than the volume of the cake j.

Babaei wants to prepare a birthday cake that has a maximum possible total volume. Help him find this value.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 100 000) — the number of simple cakes Babaei has.

Each of the following n lines contains two integers ri and hi (1 ≤ ri, hi ≤ 10 000), giving the radius and height of the i-th cake.

Output

Print the maximum volume of the cake that Babaei can make. Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6.

Namely: let's assume that your answer is a, and the answer of the jury is b. The checker program will consider your answer correct, if .

Examples
input
2
100 30
40 10
output
942477.796077000
input
4
1 1
9 7
1 4
10 7
output
3983.539484752
Note

In first sample, the optimal way is to choose the cake number 1.

In second sample, the way to get the maximum volume is to use cakes with indices 12 and 4.



注意update函数赋值的那个形参val必须为long long,防止出现溢出错误。

对于任意位置i,求出1~i-1的区间内比a[i]小的数中的最大值,想到用线段树维护。

建立线段树有两种方法:

1种是按照原数组建立从1~n的线段树,遍历新数组。这时结构体排序要注意数值相等的相邻的数字,原有下标按照降序排列。

原因是这样在遍历排序后的数组时,线段树中[1,i)区间中不会因为有和自己数值相等,但下标小的元素影响区间最大值。

如果按照升序排序会WA

2.按照新数组建立从1~n的线段树,遍历原有数组。线段树查询区间的为1到比lower_bound(...)-1的位置。遍历原数组的好处是只用查找当前线段数中比a[i]小的所有元素的区间。不会有相同数值的元素影响结果的情况。


用map或者set也可以解。

用map的时候,建立<a[i],dp[a[i]>的map。用map的lower_bound函数找到比mp下标小的index中,dp值最大的item。

注意map会自动按照key的从小到大顺序排列。在处理的时候需要从lower_bound到end位置依次遍历,除去后面key>=a[i]的,且dp值小于等于当前位置dp值的item。这样才可以始终保证map通过lower_bound查找出来的那个lower_bound-1位置的dp值是前面最大的。

例子:5 1 6 2 5 7,5 2 3 2 3,5 1 6 2 3 4 5 7。第一个例子是要注意,当5处在a[1]位置时,后面再次出现5时,dp值会大于5。所以删除时与该元素key相同的元素,可能value更小。第二个例子中3的dp值始终为5,两个3的dp值是相等的。第一个例子中第2个5出现时,需要删除6 7这个元素,这样才保证局部最大。即保证了map中key升序的同时,value也升序。单调性。


#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define CLR(x) memset(x,0,sizeof(x))
#define ll long long
using namespace std;

int n;
ll tree[100010<<2],a[100010],b[100010],dp[100010];

void PushUp(int rt){
  tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}

void build(int l,int r,int rt){
   if(l==r){tree[rt]=0;return;}
   int m=(l+r)>>1;
   build(l,m,rt<<1);
   build(m+1,r,rt<<1|1);
   PushUp(rt);
}

void update(int p,ll val,int l,int r,int rt){
   if(l==r){tree[rt]=val;return;}
   int m=(l+r)>>1;
   if(p<=m) update(p,val,l,m,rt<<1);
   else update(p,val,m+1,r,rt<<1|1);
   PushUp(rt);
}

ll query(int L,int R,int l,int r,int rt){
   if(L<=l&&r<=R) return tree[rt];
   int m=(l+r)>>1;
   ll ans=0;
   if(L<=m) ans=max(ans,query(L,R,l,m,rt<<1));
   if(R>m) ans=max(ans,query(L,R,m+1,r,rt<<1|1));
   return ans;
}

int main(){
   while(~scanf("%d",&n)){
     for(int i=1;i<=n;i++){
        int r,h;
        scanf("%d%d",&r,&h);
        a[i]=b[i]=(ll)r*r*h;
     }
     sort(a+1,a+n+1);
     build(1,n,1);
     for(int i=1;i<=n;i++){
        int pos=lower_bound(a+1,a+n+1,b[i])-a;
        if(pos==1) dp[i]=b[i];
        else dp[i]=query(1,pos-1,1,n,1)+b[i];
        update(pos,dp[i],1,n,1);
     }
     printf("%.12f\n",M_PI*tree[1]);
   }
}



#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define CLR(x) memset(x,0,sizeof(x))
#define ll long long
using namespace std;

struct node
{
    ll v;
    int id;
}a[100010];
int n;
ll tree[100010<<2],dp[100010];

bool cmp(node a,node b){
    if(a.v==b.v)
        return a.id>b.id;
    return a.v<b.v;
}

void PushUp(int rt){
  tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}

void build(int l,int r,int rt){
   if(l==r){
     tree[rt]=0;
     return;
   }
   int m=(l+r)>>1;
   build(l,m,rt<<1);
   build(m+1,r,rt<<1|1);
   PushUp(rt);
}

void update(int p,ll val,int l,int r,int rt){
   if(l==r){
     tree[rt]=val;
     return;
   }
   int m=(l+r)>>1;
   if(p<=m){
     update(p,val,l,m,rt<<1);
   }else{
     update(p,val,m+1,r,rt<<1|1);
   }
   PushUp(rt);
}

ll query(int L,int R,int l,int r,int rt){
   if(L<=l&&r<=R){
     return tree[rt];
   }
   int m=(l+r)>>1;
   ll ans=0;
   if(L<=m){
     ans=max(ans,query(L,R,l,m,rt<<1));
   }
   if(R>m){
     ans=max(ans,query(L,R,m+1,r,rt<<1|1));
   }
   return ans;
}

int main(){
   while(~scanf("%d",&n)){
     for(int i=1;i<=n;i++){
        int r,h;
        scanf("%d%d",&r,&h);
        a[i].v=(ll)r*r*h;
        a[i].id=i;
     }
     sort(a+1,a+n+1,cmp);
     build(1,n,1);
     ll ans=0;
     for(int i=1;i<=n;i++){
        if(a[i].id==1){
            dp[a[i].id]=a[i].v;
        }else{
            dp[a[i].id]=query(1,a[i].id-1,1,n,1)+a[i].v;
        }
        update(a[i].id,dp[a[i].id],1,n,1);
        ans=max(ans,dp[a[i].id]);
     }
     printf("%.9f\n",M_PI*ans);
   }
}


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

#define ll long long
#define pi acos(-1.0)
#define maxn 100100


int main(){
	int r,h,n;
	scanf("%d",&n);
	map<ll,ll> mp;
	mp[0]=0;
	for (int i=1;i<=n;i++){
		scanf("%d%d",&r,&h);
		ll v=(ll)r*r*h;
		map<ll,ll>::iterator r=mp.lower_bound(v);
		ll sv=(--r)->second+v;
        for(r++;r!=mp.end()&&r->second<=sv;)
           mp.erase(r++);
		mp[v]=sv;
	}
	printf("%.10f\n",(--mp.end())->second * pi);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值