题目:http://codeforces.com/contest/629/problem/D
题意:给定一个长度为n(n<100000)的序列,求找一个严格升序的子序列使得子序列的和最大。
分析:
定义dp[sum]表示和为sum,以dp[sum]结尾的最小体积。(类似最长上升子序列的做法)
我们发现sum越小,那么dp[sum]就越小,否则,dp[sum]不够优,就不满足定义。
所以dp[sum]数组是具有单调性的。
维护dp[sum]数组的话,用一个set维护。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1ll<<62;
const LL MINT = ~0u>>1;
const int maxn = 123456;
struct node
{
LL dp;
LL lastValue;
node(LL a=0,LL b=0):
dp(a),lastValue(b){}
bool operator < (const node &nd)const
{
return dp<nd.dp;
}
};
set <node> st;
LL a[maxn],dp[maxn];
void process(LL dp,LL lv)
{
/*
更新set
若lv比 lv_2 大 ,要插入set的条件是dp>dp_2
若lv比lv_2小,且dp>dp_2那么set里面的有些节点要被删掉
*/
if(st.empty())
{
st.insert(node(dp,lv));
return ;
}
node newnode=node(dp,lv);
typedef set <node>::iterator itor;
itor it = st.lower_bound(newnode);
if(it==st.end())
{
itor temp = it;
itor per = --temp;
while(per!=st.begin() && per->lastValue>=lv)
st.erase(per--);
if(per==st.begin() && per->lastValue>=lv)
st.erase(per);
st.insert(newnode);
}
else
{
if(it==st.begin())
{
if(it->dp==dp)
{
if(it->lastValue<=lv)
return ;
st.erase(it);
st.insert(newnode);
}
else
{
if(it->lastValue>lv)
{
st.insert(newnode);
}
else
{
return ;
}
}
}
else
{
if(it->lastValue<=lv)
return ;
itor per = it;
if(it->dp!=dp)
--per;
while(per!=st.begin() && per->lastValue>=lv)
st.erase(per--);
if(per==st.begin() && per->lastValue>=lv)
st.erase(per);
st.insert(newnode);
}
}
}
LL Find(LL x)
{
typedef set <node>::iterator itor;
LL down=0,mid,up=INF;
LL ret(0);
while(down<=up)
{
mid=(down+up)/2;
// printf("mid:%lld\n",mid);
itor it = st.lower_bound(node(mid));
if(it!=st.end() && it->lastValue<x)
{
down = mid + 1;
if(it->dp>ret)
ret=it->dp;
}
else
{
up= mid -1;
}
}
return ret;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int r,h;
scanf("%d%d",&r,&h);
a[i]=r*1ll*r*h;
}
LL ans(0);
for(int i=1;i<=n;i++)
{
LL f=Find(a[i])+a[i];
ans=max(ans,f);
process(f,a[i]);
}
printf("%.10lf",ans*1.0*acos(-1));
return 0;
}
解法二
分析:将体积离散化之后,作为线段树的节点编号,每个节点存区间的最大体积和就ok了,容易理解,代码又好敲。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1ll<<62;
const LL MINT = ~0u>>1;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 2e5;
LL a[maxn],uniq[maxn]; //体积
LL tree[maxn<<2]; //线段树维护体积和
void update(int pos,LL v,int l,int r,int rt)
{
if(l==r)
{
tree[rt]=max(tree[rt],v);
return ;
}
int m=(l+r)>>1;
if(pos<=m)
update(pos,v,lson);
else
update(pos,v,rson);
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
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 ret(-INF);
if(L<=m)
ret=max(ret,query(L,R,lson));
if(R>m)
ret=max(ret,query(L,R,rson));
return ret;
}
int Find(LL f,int n)
{
int down=1,mid,up=n;
while(down<=up)
{
mid = (down+up)>>1;
if(uniq[mid]==f) return mid;
if(uniq[mid]>f) up=mid-1;
else down=mid+1;
}
return -1;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int r,h;
scanf("%d%d",&r,&h);
uniq[i]=a[i]=r*1ll*r*h;
}
sort(uniq+1,uniq+1+n);
int tail=unique(uniq+1,uniq+n+1)-uniq-1;
LL ans(0);
for(int i=1;i<=n;i++)
{
int index=Find(a[i],tail);
LL sum(a[i]);
if(index>1)
sum+=query(1,index-1,1,n,1);
ans=max(ans,sum);
update(index,sum,1,n,1);
}
printf("%.10lf\n",ans*acos(-1));
return 0;
}