求上升子序列最大和对原始n^2的算法优化,是对内层for循环优化。
内层for循环求的是 dp[ i ] = max ( dp[ 1 ] ......dp[ i-1 ] ) ; 可以看出是区间
查询最大值。理论上线段树 log n 查询时间, 修改也是单点修改 (就是更新
dp[ i ] 的值 ) 。这样的操作还可以用树状数组,复杂度一样,代码更简单。
突发奇想,这不是RMQ吗,理论上平方分割也是可行的,不过总复杂度是
n*sqrt( n ) 。线段树跑了109毫秒,树状数组78毫秒,题目给2秒,我算了下
n*sqrt( n ) 应该可以过,就写了一发。结果108毫秒是smg。下面附上AC代码
(要引用代码的记得加头文件和using namespace std ;)。
线段树:
typedef long long ll;
const int MAXN=100000+5;
const double pi=acos(-1);
int n;
ll a[MAXN];
ll x[MAXN];
ll dp[MAXN];
struct node{
int l,r;
ll Max;
}tree[MAXN*4];
void build( int pos,int l,int r ){
int mid=(l+r)/2;
tree[pos].l=l;
tree[pos].r=r;
if(l==r){
tree[pos].Max=0;
return;
}
build(pos*2,l,mid);
build( pos*2+1,mid+1,r );
tree[pos].Max=max( tree[pos*2].Max,tree[pos*2+1].Max );
}
ll query(int pos,int l,int r){
int L=tree[pos].l;
int R=tree[pos].r;
int mid=(L+R)/2;
if(l>r)return 0;
if( (l==tree[pos].l)&&(r==tree[pos].r) ){
return tree[pos].Max;
}
else if( r<=mid ){
return query( pos*2,l,r );
}
else if( l>=mid+1 ){
return query( pos*2+1,l,r );
}
else {
return max( query(pos*2,l,mid),query(pos*2+1,mid+1,r) );
}
}
void update( int pos, int k,ll p ){
int L=tree[pos].l;
int R=tree[pos].r;
int mid=(L+R)/2;
if(L==R){
tree[pos].Max=p;
return ;
}
else if(k<=mid){
update(pos*2,k,p);
}
else {
update( pos*2+1,k,p );
}
tree[pos].Max=max( tree[pos*2].Max,tree[pos*2+1].Max );
}
int main()
{
scanf("%d",&n);
ll r,h;
for(int i=1;i<=n;i++){
scanf("%I64d%I64d",&r,&h);
a[i]=r*r*h;
x[i-1]=a[i];
}
sort(x,x+n);
ll ans=0;
build(1,1,n);
for(int i=1;i<=n;i++){
dp[i]=a[i];
int tmp=lower_bound(x,x+n,a[i])-x;
dp[i]=max( dp[i],query(1,1,tmp) +dp[i] );
update(1,tmp+1,dp[i]);
ans=max(ans,dp[i]);
}
printf("%.15f\n",ans*pi);
return 0;
}
树状数组:
typedef long long ll;
const int MAXN=100000+5;
const double pi=acos(-1);
int n;
ll a[MAXN];
ll x[MAXN];
ll dp[MAXN];
ll bit[MAXN];
ll sum(int x){
ll ans=0;
while(x>0){
ans=max(ans,bit[x]);
x-=x&-x;
}
return ans;
}
void add(int x,ll p){
while(x<=n){
bit[x]=max(bit[x],p);
x+=x&-x;
}
}
int main()
{
scanf("%d",&n);
ll r,h;
for(int i=1;i<=n;i++){
scanf("%I64d%I64d",&r,&h);
a[i]=r*r*h;
x[i-1]=a[i];
}
ll ans=0;
sort(x,x+n);
for(int i=1;i<=n;i++){
dp[i]=a[i];
int tmp=lower_bound( x,x+n,a[i] )-x;
dp[i]=max( dp[i], dp[i]+sum(tmp) );
add(tmp+1,dp[i]);
ans=max(ans,dp[i]);
}
printf("%.15f\n",ans*pi);
return 0;
}
平方分割:
typedef long long ll;
const int MAXN=100000+5;
const double pi=acos(-1);
ll a[MAXN];
ll dp[MAXN];
ll x[MAXN];
ll bit[MAXN];
int cnt,siz;
int n;
ll num[1000];
ll solve(int x){
int geshu=x/siz;
int p=x-geshu*siz;
ll ans=0;
for(int i=geshu*siz+1;i<=x;i++){
ans=max(ans,bit[i]);
}
for(int i=1;i<=geshu;i++){
ans=max(ans,num[i]);
}
return ans;
}
void add(int x,ll p){
bit[x]=p;
int tmp=x/siz;
if(x%siz)
num[tmp+1]=max(num[tmp+1],p);
else
num[tmp]=max(num[tmp],p);
}
int main()
{
scanf("%d",&n);
ll r,h;
for(int i=1;i<=n;i++){
scanf("%I64d%I64d",&r,&h);
a[i]=r*r*h;
x[i-1]=a[i];
}
sort(x,x+n);
siz=(int ) sqrt(n);
cnt=n/siz;
ll ans=0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++){
dp[i]=a[i];
int tmp=lower_bound(x,x+n,a[i])-x;
dp[i]=max(dp[i],solve(tmp)+dp[i]);
add(tmp+1,dp[i]);
ans=max(ans,dp[i]);
}
printf("%.15f\n",ans*pi);
return 0;
}