bzoj 1492

这道题真好。。。

首先,感觉像DP,但是如果按照原题意,有无数个状态,每个状态又有无数个转移。

然后思考,我们每次买一部分和卖一部分的原因是什么,如果没有那个比例(就是rate=1恒成立),那么很容易贪心证明每次必须买完或卖完,但加了比例后就没那么好证明了,感觉一下吧。

然后就可以写DP方程了(dp[i]表示通过前i天的交易,到达第i天时,身上最多的钱)

(内层的max前面那项很好解决主要是后面的那个转移,所以后面就之考虑后面那个)

这个转移中有i的信息与j的信息相乘的项,所以考虑是否可用斜率优化,设有两项:k<j<i,那么“选j比选k优”当且仅当:

我们化简:

设:

那么就是:

走到这我们就走不动了,因为f或g函数没有单调性,我们就没办法像普通的斜率优化那样除过去。这时我们回过头,发现我们设的k<j<i没有什么用,对上面那个式子的化简没有什么用,想想后,恍然大悟,我们以前斜率优化的时候,之所以设k<j<i是因为上面的f函数或g函数是单调的,我们设了k<j<i的目的主要是为了让g[j]-g[k]或f[j]-f[k]的正负恒定,这样就可以除过去了,这道题,我们不妨不设k<j<i,而直接设j<i,k<i,且g[j]>g[k](当然也可以设g[j]<g[k],f[i]>f[k]或f[i]<f[k]),这样上面那个式子就可以化简了:

是不是很像斜率优化,和斜率优化一样,我们把每个决策点j看成是一个点:(g[j],f[j]),容易证明,最优决策点一定是上凸壳上的一个点:

以上图为例子,当前计算的状态是i(其对应斜率-b[i]/a[i]为绿线的斜率),后很多决策点(红点),上凸壳上的点已经标号。此时最有决策点是B,最优决策点的左右两条直线的斜率把当前状态的斜率卡在中间。

这道题和普通的斜率优化不同的地方在于,普通的斜率优化的状态的斜率是单调的(即绿线的斜率),并且每个状态作为决策点的横坐标也是单调的(即每次新加入的蓝点的横坐标是单调的),这就让我们可以均摊O(1)地插入一个点到凸包并且O(1)地找到我们的最优决策点。

但这道题就不行,上面两个性质它都不满足,所以一般的思路是用平衡树维护一个上凸壳,这样插入一个决策点和找一个最有决策点的复杂度都是O(logn)的,可以搞定这道题,但我没写过动态维护凸壳,听说难写难调。

然后这道题就成了时间分治(cdq分治?)的例题。

时间分治是这样的:对于一个序列:

ABABAABBBABABABABABABAAB

其中B是一个询问,其答案取决于其前面的A,并且A对B的影响独立(即可以用单个A就可以更新B,只要前面的A都更新过B,那么B的答案就是正确的)。那么就可以时间分治了。对于上面那个序列,我们先拆成两半:

1、ABABAABBBABA

2、BABABABABAAB

我们解决了1、2两个子问题后,合并时只要把1中的A对2中的B的影响更新到B,那么就可以了。

ABABAABBBABA BABABABABAAB

即只要用红色的A去更新蓝色的B,那么当前的任务就完成了,将有颜色的部分提出来,我们发现A全在B前面,就是说我们只需解决“先给出所有A,再给出所有B”这个问题就可以了。(这个就提供给我们了一个将ABABAB问题并且满足上面那个影响独立性质的问题以一个log的复杂度变成AAABBB问题)

至于这道题,我们的A是给出一个点,我们的B是前面的所有点选一个最优决策点来更新B,因为只要我们把可能最优的A去更新B,那么B就一定会得到最优答案,所以满足“影响独立”原则,这道题有个细节要注意,就是我们的A和B是合在一起的,并且只有知道了B的答案,才知道A,也就是说我们每次分治时,要先解决左边的子问题,然后用左边的决策点去更新右边的询问点,在解决右边的子问题。

感谢xhr和cdq的论文。

  1 /**************************************************************
  2     Problem: 1492
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:1564 ms
  7     Memory:11508 kb
  8 ****************************************************************/
  9
 10 /*
 11     dp[1] = s
 12     dp[i] = max{ dp[i], dp[j]*(a[i]*r[j]+b[i])/(a[j]*r[j]+b[j]) | j in [1,i) } i in [2,n]
 13
 14     f[i] = (dp[i]*r[i])/(a[i]*r[i]+b[i])
 15     g[i] = dp[i]/(a[i]*r[i]+b[i])
 16     dp[i] = max{ dp[i], f[j]*a[i]+g[j]*b[i] | j in [1,i) } i in [2,n]
 17     ( g[i], f[i] ) as point
 18     k[i] = -b[i]/a[i]
 19 */
 20 #include <cstdio>
 21 #include <cmath>
 22 #include <iostream>
 23 #include <vector>
 24 #include <algorithm>
 25 #define N 100010
 26 #define eps 1e-10
 27 #define fprintf(...)
 28 using namespace std;
 29
 30 int sg( double x ) { return (x>-eps)-(x<eps); }
 31 struct Vector {
 32     double x, y;
 33     Vector(){}
 34     Vector( double x, double y ):x(x),y(y){}
 35     Vector operator+( const Vector &b ) const { return Vector(x+b.x,y+b.y); }
 36     Vector operator-( const Vector &b ) const { return Vector(x-b.x,y-b.y); }
 37     Vector operator*( double b ) const { return Vector(x*b,y*b); }
 38     Vector operator/( double b ) const { return Vector(x/b,y/b); }
 39     double operator^( const Vector &b ) const { return x*b.y-y*b.x; }
 40     double operator&( const Vector &b ) const { return x*b.x+y*b.y; }
 41     double ang() { return atan2(y,x); }
 42     bool operator<( const Vector &b ) const { return x<b.x||(x==b.x&&y<b.y); }
 43 };
 44 typedef Vector Point;
 45
 46 int n;
 47 double s;
 48 double aa[N], bb[N], rr[N], kk[N];
 49 double f[N], g[N], dp[N];
 50 double ag[N];
 51
 52 bool onleft( Point &a, Point &b, Point &c ) {
 53     return sg( (b-a)^(c-a) ) > 0;
 54 }
 55 void convex( vector<Point> &p, vector<Point> &c ) { //  up convex
 56     sort( p.begin(), p.end() );
 57     c.push_back( p.back() );
 58     for( int i=p.size()-2; i>=0; i-- ) {
 59         while( c.size()>1 && !onleft( c[c.size()-2], c[c.size()-1], p[i] ) )
 60             c.pop_back();
 61         c.push_back( p[i] );
 62     }
 63 }
 64 bool cmp_k( int a, int b ) {
 65     return kk[a]>kk[b];
 66 }
 67 void cdq( int lf, int rg, vector<Point> &c ) {
 68     if( lf==rg ) {
 69         int i=lf;
 70         dp[i] = max( dp[i], s );
 71         s = max( dp[i], s );
 72         g[i] = dp[i]/(aa[i]*rr[i]+bb[i]);
 73         f[i] = rr[i]*g[i];
 74         c.push_back( Point(g[i],f[i]) );
 75 //      fprintf( stderr, "dp[%d] = %lf (%.2lf,%.2lf) %.2lf\n", i, dp[i], g[i], f[i], kk[i] );
 76 //      fprintf( stderr, "i=%d dp=%.2lf f=%.2lf g=%.2lf\n",
 77 //              i, dp[i], f[i], g[i] );
 78         return;
 79     }
 80     int mid=(lf+rg)>>1;
 81     vector<Point> cl, cr;
 82     vector<int> vr;
 83     cdq( lf, mid, cl );
 84     for( int i=0; i<cl.size()-1; i++ ) {
 85         Vector u = cl[i+1]-cl[i];
 86         if( sg(u.x)==0 ) {
 87             if( u.y>0.0 )
 88                 ag[i] = 1e20;
 89             else
 90                 ag[i] = -1e20;
 91         } else
 92             ag[i] = u.y/u.x;
 93     }
 94     for( int i=mid+1; i<=rg; i++ )
 95         vr.push_back( i );
 96     sort( vr.begin(), vr.end(), cmp_k );
 97     for( int i=0,j=0; j<vr.size(); j++ ) {
 98         int k=vr[j];
 99         while( i<cl.size()-1 && ag[i]>kk[k] ) i++;
100         dp[k] = max( dp[k], cl[i].x*bb[k]+cl[i].y*aa[k] );
101     }
102     cdq( mid+1, rg, cr );
103     for( int i=0; i<cr.size(); i++ )
104         cl.push_back( cr[i] );
105     convex( cl, c );
106     reverse( c.begin(), c.end() );
107 }
108 int main() {
109     scanf( "%d%lf", &n, &s );
110     for( int i=1; i<=n; i++ ) {
111         scanf( "%lf%lf%lf", aa+i, bb+i, rr+i );
112         kk[i] = -bb[i]/aa[i];
113     }
114     vector<Point> c;
115     cdq(1,n,c);
116     double ans = 0.0;
117     for( int i=1; i<=n; i++ )
118         ans = max( ans, dp[i] );
119     printf( "%.3lf\n", ans );
120 }
121
122 

时间: 2024-09-28 05:09:42

bzoj 1492的相关文章

BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )

dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡树维护上凸壳, 询问时就在凸壳上二分...时间复杂度O(NlogN) ----------------------------------------------------------------------------------------------- #include<cmath> #i

【BZOJ 1492】 [NOI2007]货币兑换Cash

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 1948  Solved: 879 [Submit][Status][Discuss] Description Input 第一行两个正整数N.S,分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来N 行,第K 行三个实数AK.BK.RateK,意义如题目中所述 Output 只有一个实数MaxProfit,表示第N 天的操作结束时能够获得的最大的金

bzoj 1492 [NOI2007]货币兑换Cash(斜率dp+cdq分治)

Description Input 第一行两个正整数N.S,分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来N 行,第K 行三个实数AK.BK.RateK,意义如题目中所述 Output 只有一个实数MaxProfit,表示第N 天的操作结束时能够获得的最大的金钱 数目.答案保留3 位小数. Sample Input 3 100 1 1 1 1 2 2 2 2 3 Sample Output 225.000 HINT 测试数据设计使得精度误差不会超过10-7.对于40%的测试数据,满足N

BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或者留言我. 首先这道题能推出个表达式 f[i]代表第i天最大收益. xx[i]表示将第i天的钱都买A的数量 yy[i]表示将第i天的钱都买B的数量 所以f[i]=max(f[i?1],p[i].a?xx[j]+p[i].b?yy[j])j<i 所以我们要维护这个n^2的递推式 又知道f[i]是由小于

[BZOJ 1492][NOI2007]货币兑换Cash(CDQ分治+斜率优化Dp)

Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目.我们记录第 K 天中 A券 和 B券 的 价值分别为 AK 和 BK(元/单位金券).为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法 .比例交易法分为两个方面:(a)卖出金券:顾客提

bzoj 1492: [NOI2007]货币兑换Cash

Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目.我们记录第 K 天中 A券 和 B券 的 价值分别为 AK 和 BK(元/单位金券).为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法 .比例交易法分为两个方面:(a)卖出金券:顾客提

BZOJ 1492 NOI 2007 货币兑换Cash CDQ分治+斜率优化DP

题目大意:有两种金券,A和B.每一天有一个rate值,表示购入的比例:还有每一天AB金券的售价.现在给出初始的钱数,问最后能够获得多少钱. 思路:这算是神题了吧,啃论文啃别人代码将近一天才算有点明白. 首先题目中说的可以买一部分或者卖一部分是扯淡的,因为为了最大获利一定要全部买入,全部卖出.朴素的DP方程就好弄了. 设f[i]为第i天最多的B券的数量.那么f[i] = (rate[j] * f[j] * a[i] + f[j] * b[i]) / (rate[i] * a[i] + b[i])

【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP

先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为维护凸壳,一种根据两点的斜率与某一常数的大小关系推断二者的优劣,一种将转移方程化为相关直线方程,通过取得最大(小)截距来求最优解.关于其实现方法上,当点的x坐标单调时,可依据比较常数是否单调选择单调队列或单调栈,而当其x坐标不单调时常常使用CDQ分治或平衡树来实现. 千万别用替罪羊来写动态凸壳!!!

bzoj 2726: [SDOI2012]任务安排【cdq+斜率优化】

cdq复健.jpg 首先列个n方递推,设sf是f的前缀和,st是t的前缀和: \[ f[i]=min(f[j]+s*(sf[n]-sf[j])+st[i]*(sf[i]-sf[j])) \] 然后移项: \[ f[i]=f[j]+s*sf[n]-s*sf[j]+st[i]*sf[i]-st[i]*sf[j] \] \[ f[i]=f[j]+s*sf[n]+st[i]*sf[i]-s*sf[j]-st[i]*sf[j] \] \[ f[i]=f[j]+s*sf[n]+st[i]*sf[i]-sf[