官方题解:
首先,我们计算每个蛋糕的体积: vi=π∗hi∗r2i
现在,考虑序列 v1,v2,v3,…,vn :问题的答案是这个序列中递增子序列的最大和。我们怎么样解决它?
首先去掉小数,我们可以定义一个新的数组 a1,a2,a3,…,an,ai=vi/π=hi∗r2i
们考虑 dpi是以 ai结束的序列和的最大值且
dpi=max(ai,maxj<i,aj≤aidp[j]+ai)
这个问题的答案就是: π∗maxi=1tondp[i]
现在,我们怎么计算 dpi=max(ai,maxj<i,aj≤aidp[j]+ai)?我们使用一个线段树,这个线段树有两种操作:1.将第i个数更改为v;2.找出1到i中最大的数。
现在,我们将dp与线段树结合寻找答案。
假设 a1,a2,a3,…,an已经排序好了。我们定义 bi是ai的位置。现在填充 dpi,我们找出区间 [1,bi]中最大的, 设为x,然后将线段树中 bi个位置设置成 ai+x 。问题的答案就是区间[1,n]中的最大值。
时间复杂度: O(nlogn)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef __int64 LL; const double PI=acos(-1.0); const int INF=0x3f3f3f3f; const int maxn=100005; int n; LL dp[3*maxn]; struct Cake{ LL v; int id; inline bool operator<(const Cake&a) const{//注意排序 return v<a.v||(v==a.v&&id>a.id); } }cake[maxn]; struct segmentTree{ int l; int r; }node[3*maxn]; void PushUp(int rt){ dp[rt]=max(dp[rt<<1],dp[rt<<1|1]); } void build(int rt,int l,int r){ node[rt].l=l; node[rt].r=r; if(l==r) return ; int mid=(l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } void Update(int rt,int L,int R,int pos,LL val){ if(L==pos&&R==pos){ dp[rt]=val; return ; } int mid=(L+R)>>1; if(pos<=mid) Update(rt<<1,L,mid,pos,val); else Update(rt<<1|1,mid+1,R,pos,val); PushUp(rt); } LL Query(int rt,int L,int R,int l,int r){ if(L>=l&&R<=r){ return dp[rt]; } LL res=0; int mid=(L+R)>>1; if(l<=mid) res=max(res,Query(rt<<1,L,mid,l,r)); if(r>mid) res=max(res,Query(rt<<1|1,mid+1,R,l,r)); return res; } int main(){ #ifndef ONLINE_JUDGE freopen("test.in","r",stdin); freopen("test.out","w",stdout); #endif while(~scanf("%d",&n)){ LL r,h; for(int i=1;i<=n;i++){ scanf("%I64d%I64d",&r,&h); cake[i].v=r*r*h; cake[i].id=i; } memset(dp,0,sizeof(dp)); sort(cake+1,cake+n+1); build(1,1,n); for(int i=1;i<=n;i++){ int pos=cake[i].id; LL val=Query(1,1,n,1,pos)+cake[i].v; Update(1,1,n,pos,val); } LL ans=Query(1,1,n,1,n); printf("%.10lf\n",PI*(double)ans); } return 0; }