好久以前做过这题,想的方法不够优秀,这次打cf又碰到了,看到了题解,终于理解了
首先这种题目第一感觉就是dp或者贪心
设dp[I] 表示从i开始的最大重量和
要求的是最大上升序列和
最后maxdp[I] i从1到n就是结果
关键就在于状体转移的时候如何快速找到需要的状态dp[I] = max{dp[j] v[j] >v[i] j > i}
要找到后面的圆柱体中体积比他大的中总体积最大的,我的一个思路是:比如从i+1开始找,找到一个v[j]>v[i] 那么后面就应该找v[k]<v[j]的,然后做初始化……这样利用了dp的性质减少了部分计算,但是复杂度没有变化
下面介绍用线段树优化的方法
首先做一个排序,得到每个v[i]在排序后序列中的位置p[i](应该去掉一样大的元素)
这样每次只要找p[i]前面的元素,并且总体积已经算过的,最大值就可以了
线段树初始化的时候每个都是0,算过总体积的,就更新线段树,这样,每次找的范围是一定的,就能利用线段树再logn时间完成转移
总复杂度为nlogn
应该想到排序,然后找前面的中的最值,就可以联系到线段树了!排序这种东西,不排白不排
上AC代码,居然1A,神奇
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const double pai = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384;
typedef long long LL;
LL r,h;
LL v[100005];
struct cy
{
LL v;
LL rank;
}a[100005];
bool compare(cy x,cy y)
{
return (x.v < y.v);
}
int b[100005];//从1- (b【i】 - 1 )找最大值
LL seg[500000];
int c[100005];
int n;
void init(int n_)
{
n = 1;
while(n < n_) n*=2;
for(int i = 0; i < 2 * n - 1; i++)seg[i] = 0;
}
void update(int k,LL a)//k也从0开始
{
k += n - 1;
seg[k] = a;
while(k > 0){
k = (k - 1)/2;
seg[k] = max(seg[k*2+ 1],seg[k * 2 + 2]);
}
}
LL query(int a,int b,int k,int l,int r){
if (r <= a || b <= l) return 0;
if (a <= l && r <= b) return seg[k];
else{
LL vl = query(a,b,k*2+1,l,(l + r)/2);
LL vr = query(a,b,k*2+2,(l + r)/2,r);
return max(vl,vr);
}
}
LL dp[200000];
int main()
{
int nn;
cin >> nn;
for(int i = 0; i < nn; i++){
cin >> r >> h;
a[i].v = r * r * h;
a[i].rank = i;
v[i] = a[i].v;
}
sort(a , a + nn,compare);
b[a[nn - 1].rank] = nn - 1;
c[a[nn - 1].rank] = nn - 1;
int tem = 1;
for(int i = nn - 2; i >= 0; i--){
if (a[i].v == a[i + 1].v){
b[a[i].rank] = b[a[i + 1].rank];
tem++;
}
else {
b[a[i].rank] = b[a[i + 1].rank] - tem;
tem = 1;
}
c[a[i].rank] = i;
}
init(nn);
for(int i = nn - 1; i >= 0; i--){
//cout << v[i] << ' ' << b[i] << ' ' << c[i] << ' ';
dp[i] = 0;
dp[i] = query(b[i] + 1,n,0,0,n) + v[i];
update(c[i],dp[i]);
//cout << dp[i] << '\n';
}
LL ans = 0;
for(int i = 0; i < nn; i++)
ans = max(dp[i],ans);
printf("%lf\n",pai*ans);
}