zoukankan      html  css  js  c++  java
  • BZOJ 1492 [NOI2007]货币兑换Cash (CDQ分治/splay 维护凸包)

    题目大意:太长了略

    splay调了两天一直WA弃疗了

    首先,我们可以一个贪心,如果买/卖,就一定都买/卖掉,否则不买/卖

    反正货币的行情都是已知的,没有任何风险,所以肯定要选择最最最优的方案了

    容易得到方程

    $dp[i]=max(dp[i-1],a[i]*frac{dp[j]*rate[j]}{rate[j]*a[j]+b[j]}+b[i]*frac{dp[j]}{rate[j]*a[j]+b[j]})$

    显然是要用凸优化了

    splay非常无脑,splay维护此题的凸包,需要找前驱,删前驱,找后继,删后继,一大堆特判...绝对恶心到吐

    所以这是一篇$CDQ$分治题解

    令$x=frac{dp[j]}{rate[j]*a[j]+b[j]},y=x*rate[j]$

    移项,可得

    $dp[i]-b[i]*x=a[i]*y$

    $y=frac{dp[i]}{a[i]}-frac{b[i]}{a[i]}x$

    发现斜率$k=-frac{b[i]}{a[i]}$是一定的,我们在外层把斜率k从小到大排序,可以优化掉一个$log$,递归时按$x$从小到大排序

    这样,递归时,每一层内部都是按$k$有序的,把这一层按照时间分为左右两个部分(不要破坏$k$的有序状态)

    先递归处理左半个区间,回溯后,左半部分的答案已知,且不会被右半部分的答案所影响

    且左半部分按$x$从小到大排序,右半部分按斜率$k$从小到大排序,取最小值,由于$k<0$,用单调栈维护一个上凸包即可

    处理完了左边对右边的贡献,递归处理右半部分

    回溯时,先处理$dp[i]=dp[i-1]$的情况,再按$x$排序,回溯到上一层

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 101000
     6 #define M1 205
     7 #define ll long long
     8 #define dd double  
     9 #define uint unsigned int
    10 #define inf 233333333
    11 #define il inline 
    12 using namespace std;
    13 
    14 const dd eps=(1e-9);
    15 int n,m;
    16 int stk[N1];
    17 dd A[N1],B[N1],r[N1],X[N1],Y[N1],K[N1],f[N1];
    18 //struct node{dd x,y,k,ans;int id;};
    19 int cmp1(int s1,int s2){return K[s1]-K[s2]<0;}
    20 int id[N1],tmp[N1];
    21 dd get_slope(int s1,int s2){
    22     if(!s2) return inf;
    23     return (Y[s1]-Y[s2])/(X[s1]-X[s2]);
    24 }
    25 void CDQ(int L,int R)
    26 {
    27     if(R-L<=1) return;
    28     int M=(L+R)>>1;
    29     int tp=0,i,j,pl=L,pr=M,k,cnt;
    30     for(int i=L;i<R;i++)
    31         if(id[i]<M) tmp[pl++]=id[i];
    32         else tmp[pr++]=id[i];
    33     for(int i=L;i<R;i++)
    34         id[i]=tmp[i];
    35     CDQ(L,M);
    36     for(i=L;i<M;i++)
    37     {
    38         k=id[i];
    39         if(tp>1&&fabs(X[stk[tp]]-X[k])<eps&&Y[k]-Y[stk[tp]]<eps) continue;
    40         while(tp>1&&get_slope(stk[tp],stk[tp-1])<=get_slope(k,stk[tp-1]))
    41             tp--;
    42         stk[++tp]=k;
    43     }
    44     for(i=M;i<R;i++)
    45         f[i]=max(f[i-1],f[i]);
    46     for(i=M;i<R;i++)
    47     {
    48         while(tp>1&&get_slope(stk[tp],stk[tp-1])<=K[id[i]])
    49             tp--;
    50         k=id[i],j=stk[tp];
    51         f[k]=max(f[k],A[k]*Y[j]+B[k]*X[j]);
    52         X[k]=f[k]/(A[k]*r[k]+B[k]);
    53         Y[k]=r[k]*X[k];
    54     }
    55     CDQ(M,R);
    56     i=L,cnt=L,j=M;
    57     while(i<M&&j<R){
    58         if(X[id[i]]<X[id[j]])
    59             tmp[cnt++]=id[i],i++;
    60         else 
    61             tmp[cnt++]=id[j],j++;
    62     }
    63     while(i<M) tmp[cnt++]=id[i],i++;
    64     while(j<R) tmp[cnt++]=id[j],j++;
    65     for(i=L;i<R;i++)
    66         id[i]=tmp[i],f[id[i]]=max(f[id[i]],f[id[i]-1]);
    67 };
    68 dd S;
    69 int main()
    70 {
    71     //freopen("t1.in","r",stdin);
    72     scanf("%d%lf",&n,&S);
    73     for(int i=1;i<=n;i++)
    74     {
    75         scanf("%lf%lf%lf",&A[i],&B[i],&r[i]);
    76         K[i]=-B[i]/A[i];id[i]=i;
    77     }
    78     f[1]=S,X[1]=S/(A[1]*r[1]+B[1]),Y[1]=X[1]*r[1];
    79     sort(id+1,id+n+1,cmp1);
    80     CDQ(1,n+1);
    81     dd ans=0;
    82     for(int i=1;i<=n;i++)
    83         ans=max(ans,f[i]);
    84     printf("%.3lf
    ",ans);
    85     return 0;
    86 }
  • 相关阅读:
    动态规划(0-1背包)---划分数组为和相等的两部分
    动态规划(0-1背包)
    动态规划(最长递增子序列)---最长公共子序列
    动态规划(最长递增子序列)---最长摆动子序列
    动态规划(最长递增子序列)---最长递增子序列
    动态规划(最长递增子序列)
    动态规划(分割整数)---分割整数构成字母字符串
    浅谈进程同步和互斥的概念
    如何由Height Map生成Normal Map
    3D中的切线空间简介
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10139253.html
Copyright © 2011-2022 走看看