【BZOJ1492】【NOI2007】—Cash(cdq分治维护凸包优化斜率dp)

传送门

考虑令f[i]f[i]f[i]为第iii天得到的最多的AAA券,g[i]g[i]g[i]为第iii天得到的最多的BBB
g[i]=f[i]/rate[i]g[i]=f[i]/rate[i]g[i]=f[i]/rate[i]

那显然有个n2dpn^2dpn2dp,暴力枚举前一天转移
但显然这样得不到满分

考虑如果前面i,ji,ji,j两天对于决策nownownow的影响,如果iiijjj优的话
(f[i]−f[j])∗a[now]−(g[i]−g[j])∗b[now]>0(f[i]-f[j])*a[now]-(g[i]-g[j])*b[now]>0(f[i]f[j])a[now](g[i]g[j])b[now]>0

g[i]−g[j]f[i]−f[j]&lt;−a[now]b[now]\frac{g[i]-g[j]}{f[i]-f[j]}&lt;-\frac{a[now]}{b[now]}f[i]f[j]g[i]g[j]<b[now]a[now]

注意特判一个f[i]=f[j]f[i]=f[j]f[i]=f[j]的情况

如果将(f[i],g[i])(f[i],g[i])(f[i],g[i])作为平面上的点,那也就是我们维护一个上凸壳

则对于每一个nownownow,我们找到最后一个满足相邻2点斜率小于−a[now]b[now]-\frac{a[now]}{b[now]}b[now]a[now]的点,并把这个点更新当前节点的答案

但我们发现这个无法二分求
考虑splay动态维护 cdqcdqcdq分治维护凸包

具体的我们可以使nownownow保持有序
就可以一边扫凸包一边更新了

可以大力sortsortsort,不过要多一个logloglog
也可以cdqcdqcdq分治中途把凸包和−a[now]b[now]-\frac{a[now]}{b[now]}b[now]a[now]顺带归并排序

复杂度O(nlogn)O(nlogn)O(nlogn),似乎不比每次sortsortsort快多少……

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();
    int res=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
    return res*f;
}
#define db double
const double eps=1e-8; 
#define inf 0x7fffffff
const int N=100005;
int n,top,stk[N],id[N],tmp[N],idx[N],tmpk[N],bel[N];
db ans[N],f[N],g[N],ak[N],bk[N],rate[N];
inline db max(db a,db b){return a>b?a:b;}
inline int check(db k){
    return (k>-eps)-(k<eps);
}
inline bool comp(int a,int b){
    return f[a]<f[b]||(check(f[a]-f[b])==0&&g[a]<g[b]);
}
inline double k(int a,int b){
    if(check(f[a]-f[b])==0)return check(g[a]-g[b])*inf;
    return ((g[a]-g[b])/(f[a]-f[b]));
}
inline bool compk(int a,int b){
    return check((-ak[a]/bk[a])-(-ak[b]/bk[b]))>0;
}
void cdq(int l,int r){
    if(l==r){g[l]=f[l]/rate[l];return;}
    int mid=(l+r)>>1,cnt1=l-1,cnt2=mid,cnt=l-1;
    for(int i=l;i<=r;i++)
        if(bel[idx[i]]<=mid)tmpk[++cnt1]=idx[i];
        else tmpk[++cnt2]=idx[i];
    for(int i=l;i<=r;i++)idx[i]=tmpk[i];
    cdq(l,mid);top=0;
    for(int i=l;i<=mid;i++){
        while(top>=2&&k(stk[top-1],stk[top])<k(stk[top],id[i]))top--;
        stk[++top]=id[i];
    }
    for(int now=1,i=mid+1;i<=r;i++){
        int t=bel[idx[i]];
        while(now<top&&check(k(stk[now],stk[now+1])-(-ak[t]/bk[t]))>=0)now++;
        ans[t]=max(ans[t],f[stk[now]]*ak[t]+g[stk[now]]*bk[t]);
         
    }
    for(int i=mid+1;i<=r;i++){
        int t=bel[idx[i]];ans[t]=max(ans[t],ans[t-1]),f[t]=ans[t]*rate[t]/(ak[t]*rate[t]+bk[t]);
    }   
    cdq(mid+1,r);
    cnt1=l,cnt2=mid+1,cnt=l;
    while(cnt1<=mid&&cnt2<=r)
        if(comp(id[cnt1],id[cnt2]))tmp[cnt++]=id[cnt1++];
        else tmp[cnt++]=id[cnt2++];
    while(cnt1<=mid)tmp[cnt++]=id[cnt1++];
    while(cnt2<=r)tmp[cnt++]=id[cnt2++];
    for(int i=l;i<=r;++i)id[i]=tmp[i];
}
int main(){
    n=read();scanf("%lf",&ans[1]);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]);
    f[1]=ans[1]*rate[1]/(ak[1]*rate[1]+bk[1]);
    for(int i=1;i<=n;i++)id[i]=idx[i]=bel[i]=i;
    sort(bel+1,bel+n+1,compk);
    cdq(1,n);
    printf("%.3lf",ans[n]);
}

转载于:https://www.cnblogs.com/stargazer-cyk/p/11145625.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值