zoukankan      html  css  js  c++  java
  • 【BZOJ1492】【NOI2007】—Cash(cdq分治维护凸包优化斜率dp)

    传送门

    考虑令f[i]f[i]为第ii天得到的最多的AA券,g[i]g[i]为第ii天得到的最多的BB
    g[i]=f[i]/rate[i]g[i]=f[i]/rate[i]

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

    考虑如果前面i,ji,j两天对于决策nownow的影响,如果iijj优的话
    (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]<a[now]b[now]frac{g[i]-g[j]}{f[i]-f[j]}<-frac{a[now]}{b[now]}

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

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

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

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

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

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

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

    #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]);
    }
    
  • 相关阅读:
    Array对象
    属性描述对象
    Object对象
    console对象与控制台
    编程风格
    错误处理机制
    13、百钱买百鸡之数学优化
    12、c程序中编程,统计一行中数字字符的个数。
    10、输入某年某月某日,判断这一天是这一年的第几天?
    9、c语言输入一个整数怎么分别输出它的每位上的数字
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/11145625.html
Copyright © 2011-2022 走看看