test 9 小p的单调数列 (dp+数学证明)

来源:互联网 发布:教师网络研修的好处 编辑:程序博客网 时间:2024/05/21 18:45



题解:dp+数学证明

先分析一下这个单调子序列的性质。一定存在一个最优子序列,他的单调区间的长度不超过2.

假设最优子序列有m个单调区间,序列的数字总数为s,那么最大艺术价值为s/m

设最后一个区间的数字总数为t,那么s/m的范围一定是在s-t/m-1,t之间的。为什么呢?因为是区间求平均值,所以我们只保留两个值中较大的即可。那么我们总能找出区间个数为1或2的合法最优子序列。

那么选出来的子序列就有两种可能。

一种是一个单增序列,直接求值最大的单调序列就可以了;

另一种是单增序列+单减序列,正反求然后接起来。

然后用线段树优化一下。

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#define N 100003using namespace std;int n,m,c[N],pos[N],cnt;double g[N],f[N],a[N],ans,tr[N*4];int cmp(int x,int y){return a[x]<a[y];}void update(int now){tr[now]=max(tr[now<<1],tr[now<<1|1]);}void change(int now,int l,int r,int x,double v){if (l==r) {tr[now]=max(tr[now],v); return;}int mid=(l+r)/2;if (x<=mid) change(now<<1,l,mid,x,v);else change(now<<1|1,mid+1,r,x,v);update(now);}double qjask(int now,int l,int r,int ll,int rr){if (ll>rr) return 0;if (ll<=l&&r<=rr) return tr[now];int mid=(l+r)/2;double ans=0;if (ll<=mid) ans=max(ans,qjask(now<<1,l,mid,ll,rr));if (rr>mid) ans=max(ans,qjask(now<<1|1,mid+1,r,ll,rr));return ans;}int main(){freopen("seq.in","r",stdin);freopen("my.out","w",stdout);scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%lf",&a[i]),c[i]=i;sort(c+1,c+n+1,cmp);pos[c[1]]=++cnt;for (int i=2;i<=n;i++) if (a[c[i]]!=a[c[i-1]])  pos[c[i]]=++cnt; else pos[c[i]]=cnt;f[1]=a[1]; change(1,1,cnt,pos[1],f[1]);g[n]=a[n];for (int i=2;i<=n;i++){  f[i]=qjask(1,1,cnt,1,pos[i]-1)+a[i];  change(1,1,cnt,pos[i],f[i]);    }memset(tr,0,sizeof(tr));change(1,1,cnt,pos[n],g[n]);    for (int i=n-1;i>=1;i--)    {      g[i]=qjask(1,1,cnt,1,pos[i]-1)+a[i];      change(1,1,cnt,pos[i],g[i]);    }    for (int i=1;i<=n;i++)     {     ans=max(ans,(g[i]+f[i]-a[i])/2);     ans=max(ans,f[i]);     }    printf("%.3lf",ans);}



0 0
原创粉丝点击