zoukankan      html  css  js  c++  java
  • bzoj1492 [ NOI2007 ] --斜率优化DP+cdq分治

    显然在某一天要么花完所有钱,要么不花钱。

    所以首先想到O(n^2)DP:

    f[i]=max{f[i-1],(f[j]*r[j]*a[i]+f[j]*b[i])/(a[j]*r[j]+b[j])},j<i

    其中f[j]*r[j]/(a[j]*r[j]+b[j])是第j天最多能买多少A券,B类似。

    假如我们已经找到了最优的j,那么有f[i]=(f[j]*r[j]*a[i]+f[j]*b[i])/(a[j]*r[j]+b[j])

    令x[j]=f[j]*r[j]/(a[j]*r[j]+b[j]),y[j]=f[j]/(a[j]*r[j]+b[j])

    那么有f[i]=x[j]*a[i]+y[j]*b[i]

            y[j]=(-a[i]/b[i])*x[j]+f[i]/b[i]
    这是一条斜率为-a[i]/b[i]的直线。

    由于b[i]不变,而我们要最大化f[i],所以现在我们可以将问题转化为从一些点中选出一个点,使经过这点的已知斜率的直线的截距最大

    但因为斜率和坐标都是无序的,我们用splay维护一个上凸壳,在求f[i]时从上凸壳中选出一个点j,使j点左右两边的斜率刚好将-a[i]/b[i]夹住,那么i就从j转移。

    但这题还有更神奇的cdq分治做法。

    我们可以想想,为什么斜率和坐标都是无序的呢?因为我们从1~n依次求解。那能否按照斜率排序后一一求解呢?答案是能。

    处理区间[l,r]时先将[l,mid]建成一个上凸壳,然后将[mid+1,r]按斜率排序并更新就可以了。

    时间复杂度O(n*logn)

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 #define N 100001
     8 #define Eps 1e-9
     9 #define INF 0x3f3f3f3f3f3f3f3f
    10 struct Node{
    11   double x,y,A,B,r,k;
    12   int Id;
    13 }a[N],t[N];
    14 double f[N];
    15 int i,j,St[N],Top,n,m;
    16 inline bool Cmp(Node a,Node b){return a.k<b.k;}
    17 inline double _Max(double x,double y){return x<y?y:x;}
    18 inline double Slop(int x,int y){
    19   if(!y)return -1e20;
    20   if(fabs(a[x].x-a[y].x)<Eps)return 1e20;
    21   return (a[x].y-a[y].y)/(a[x].x-a[y].x);
    22 }
    23 inline void Solve(int l,int r){
    24   if(l==r){
    25     f[l]=_Max(f[l],f[l-1]);
    26     a[l].y=f[l]/(a[l].A*a[l].r+a[l].B);
    27     a[l].x=a[l].y*a[l].r;
    28     return;
    29   }
    30   int Mid=l+r>>1;
    31   int l1=l,l2=Mid+1;
    32   for(int i=l;i<=r;i++)
    33     if(a[i].Id<=Mid)t[l1++]=a[i];else t[l2++]=a[i];
    34   for(int i=l;i<=r;i++)a[i]=t[i];
    35   Solve(l,Mid);
    36   Top=0;
    37   for(int i=l;i<=Mid;i++){
    38     while(Top>1&&Slop(St[Top-1],i)+Eps>Slop(St[Top-1],St[Top]))Top--;
    39     St[++Top]=i;
    40   }
    41   int j=1;
    42   St[++Top]=0;
    43   for(int i=r;i>Mid;i--){
    44     while(j<Top&&a[i].k<Slop(St[j],St[j+1])+Eps)j++;
    45     f[a[i].Id]=_Max(f[a[i].Id],a[St[j]].x*a[i].A+a[St[j]].y*a[i].B);
    46   }
    47   Solve(Mid+1,r);
    48   l1=l,l2=Mid+1;
    49   for(int i=l;i<=r;i++)
    50     if(l1>Mid)t[i]=a[l2++];else
    51       if(l2>r)t[i]=a[l1++];else
    52     if(a[l1].x<a[l2].x||(fabs(a[l1].x-a[l2].x)<Eps&&a[l1].y<a[l2].y))t[i]=a[l1++];else t[i]=a[l2++];
    53   for(int i=l;i<=r;i++)a[i]=t[i];
    54 }
    55 int main()
    56 {
    57   scanf("%d%lf",&n,&f[0]);
    58   for(i=1;i<=n;i++){
    59     scanf("%lf%lf%lf",&a[i].A,&a[i].B,&a[i].r);
    60     a[i].Id=i;a[i].k=-a[i].A/a[i].B;
    61   }
    62   sort(a+1,a+n+1,Cmp);
    63   Solve(1,n);
    64   printf("%.3lf",f[n]);
    65   return 0;
    66 }
    bzoj1492
  • 相关阅读:
    mysql数据库常用指令
    解决windows的mysql无法启动 服务没有报告任何错误的经验。
    “Can't open file for writing”或“operation not permitted”的解决办法
    启动Apache出现错误Port 80 in use by "Unable to open process" with PID 4!
    如何打开windows的服务services.msc
    常见的HTTP状态码 404 500 301 200
    linux系统常用的重启、关机指令
    (wifi)wifi移植之命令行调试driver和supplicant
    linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )
    alloc_chrdev_region申请一个动态主设备号,并申请一系列次设备号
  • 原文地址:https://www.cnblogs.com/gjghfd/p/6401166.html
Copyright © 2011-2022 走看看