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的论文。
/**************************************************************
Problem: 1492
User: idy002
Language: C++
Result: Accepted
Time:1564 ms
Memory:11508 kb
****************************************************************/ /*
dp[1] = s
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] f[i] = (dp[i]*r[i])/(a[i]*r[i]+b[i])
g[i] = dp[i]/(a[i]*r[i]+b[i])
dp[i] = max{ dp[i], f[j]*a[i]+g[j]*b[i] | j in [1,i) } i in [2,n]
( g[i], f[i] ) as point
k[i] = -b[i]/a[i]
*/
#include <cstdio>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#define N 100010
#define eps 1e-10
#define fprintf(...)
using namespace std; int sg( double x ) { return (x>-eps)-(x<eps); }
struct Vector {
double x, y;
Vector(){}
Vector( double x, double y ):x(x),y(y){}
Vector operator+( const Vector &b ) const { return Vector(x+b.x,y+b.y); }
Vector operator-( const Vector &b ) const { return Vector(x-b.x,y-b.y); }
Vector operator*( double b ) const { return Vector(x*b,y*b); }
Vector operator/( double b ) const { return Vector(x/b,y/b); }
double operator^( const Vector &b ) const { return x*b.y-y*b.x; }
double operator&( const Vector &b ) const { return x*b.x+y*b.y; }
double ang() { return atan2(y,x); }
bool operator<( const Vector &b ) const { return x<b.x||(x==b.x&&y<b.y); }
};
typedef Vector Point; int n;
double s;
double aa[N], bb[N], rr[N], kk[N];
double f[N], g[N], dp[N];
double ag[N]; bool onleft( Point &a, Point &b, Point &c ) {
return sg( (b-a)^(c-a) ) > ;
}
void convex( vector<Point> &p, vector<Point> &c ) { // up convex
sort( p.begin(), p.end() );
c.push_back( p.back() );
for( int i=p.size()-; i>=; i-- ) {
while( c.size()> && !onleft( c[c.size()-], c[c.size()-], p[i] ) )
c.pop_back();
c.push_back( p[i] );
}
}
bool cmp_k( int a, int b ) {
return kk[a]>kk[b];
}
void cdq( int lf, int rg, vector<Point> &c ) {
if( lf==rg ) {
int i=lf;
dp[i] = max( dp[i], s );
s = max( dp[i], s );
g[i] = dp[i]/(aa[i]*rr[i]+bb[i]);
f[i] = rr[i]*g[i];
c.push_back( Point(g[i],f[i]) );
// fprintf( stderr, "dp[%d] = %lf (%.2lf,%.2lf) %.2lf\n", i, dp[i], g[i], f[i], kk[i] );
// fprintf( stderr, "i=%d dp=%.2lf f=%.2lf g=%.2lf\n",
// i, dp[i], f[i], g[i] );
return;
}
int mid=(lf+rg)>>;
vector<Point> cl, cr;
vector<int> vr;
cdq( lf, mid, cl );
for( int i=; i<cl.size()-; i++ ) {
Vector u = cl[i+]-cl[i];
if( sg(u.x)== ) {
if( u.y>0.0 )
ag[i] = 1e20;
else
ag[i] = -1e20;
} else
ag[i] = u.y/u.x;
}
for( int i=mid+; i<=rg; i++ )
vr.push_back( i );
sort( vr.begin(), vr.end(), cmp_k );
for( int i=,j=; j<vr.size(); j++ ) {
int k=vr[j];
while( i<cl.size()- && ag[i]>kk[k] ) i++;
dp[k] = max( dp[k], cl[i].x*bb[k]+cl[i].y*aa[k] );
}
cdq( mid+, rg, cr );
for( int i=; i<cr.size(); i++ )
cl.push_back( cr[i] );
convex( cl, c );
reverse( c.begin(), c.end() );
}
int main() {
scanf( "%d%lf", &n, &s );
for( int i=; i<=n; i++ ) {
scanf( "%lf%lf%lf", aa+i, bb+i, rr+i );
kk[i] = -bb[i]/aa[i];
}
vector<Point> c;
cdq(,n,c);
double ans = 0.0;
for( int i=; i<=n; i++ )
ans = max( ans, dp[i] );
printf( "%.3lf\n", ans );
}
bzoj 1492的更多相关文章
- NOI 2007 货币兑换Cash (bzoj 1492) - 斜率优化 - 动态规划 - CDQ分治
Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个 ...
- bzoj 1492 [NOI2007]货币兑换Cash(斜率dp+cdq分治)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1492 [题意] 有AB两种货币,每天可以可以付IPi元,买到A券和B券,且A:B= ...
- BZOJ 1492 货币兑换
Description Input 第一行两个正整数\(N,S\),分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来\(N\)行,第\(K\)行三个实数\(A_{K},B_{K},Rate_{ ...
- BZOJ 1492 货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 思路: 问题转变为维护一个凸包,每次转移都找凸包上的点,并更新凸壳 可以用splay维护,或者 ...
- 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的数量. 然后就很明显了...平衡 ...
- ●BZOJ 1492 [NOI2007]货币兑换Cash
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1492 题解: 斜率优化DP,CDQ分治 定义$DP[i]$为第i天结束后的最大收益. 由于题 ...
- BZOJ.1492.[NOI2007]货币兑换(DP 斜率优化 CDQ分治/Splay)
BZOJ 洛谷 如果某天能够赚钱,那么一定会在这天把手上的金券全卖掉.同样如果某天要买,一定会把所有钱花光. 那么令\(f_i\)表示到第\(i\)天所拥有的最多钱数(此时手上没有任何金券),可以选择 ...
- 【BZOJ 1492】【NOI 2007】货币兑换Cash
这是道CDQ分治的例题: $O(n^2)$的DP: f [1]←S* Rate[1] / (A[1] * Rate[1] + B[1]) Ans←SFor i ← 2 to n For j ←1 to ...
- 斜率优化(CDQ分治,Splay平衡树):BZOJ 1492: [NOI2007]货币兑换Cash
Description Input 第一行两个正整数N.S,分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来N 行,第K 行三个实数AK.BK.RateK,意义如题目中所述 Output 只有 ...
随机推荐
- 42 - 数据库-orm-SQLAlchemy
目录 1 ORM 2 sqlalchemy 3 基本使用 3.1 创建连接 3.1.1 利用连接池执行sql 3.1.2 利用session来执行sql 3.2 创建基类 3.3 创建实体类 3.3. ...
- 简单的TCP接受在转发到客户端的套接口
//功能:客服端发送tcp包,服务器接受到并打印出来,并将包转换为大写后到客户端//2015.9.10成功 #include <stdio.h>#include <sys/socke ...
- js数组基本操作
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtm ...
- 使用Appium 测试微信小程序和微信公众号方法
由于腾讯系QQ.微信等都是基于腾讯自研X5内核,不是google原生webview,需要打开TBS内核Inspector调试功能才能用Chrome浏览器查看页面元素,并实现Appium自动化测试微信小 ...
- Postman接口&压力测试
Postman接口与压力测试实例 Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件.它提供功能强大的 Web API & HTTP 请求调试. 1.环境变量和全局 ...
- POJ 1141 Brackets Sequence(括号匹配二)
题目链接:http://poj.org/problem?id=1141 题目大意:给你一串字符串,让你补全括号,要求补得括号最少,并输出补全后的结果. 解题思路: 开始想的是利用相邻子区间,即dp[i ...
- peda的官方文档说明
peda在github上的官方文档,摘抄过来,方便查阅. 安装 git clone https://github.com/longld/peda.git ~/peda echo "sourc ...
- 20165333 2017-2018-2《Java程序设计》课程总结
一.每周作业链接汇总 1.预备作业一:我期望的师生关系 简要内容: 印象深刻的老师 我期望的师生关系 关于JAVA学习 2.预备作业二:学习基础和C语言学习基础 简要内容: 技能学习 C语言学习 关于 ...
- 集合栈计算机(UVa12096)
题目具体描述见:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_prob ...
- 淘宝IP地址库API地址
淘宝IP地址库:http://ip.taobao.com/instructions.php 接口说明 1. 请求接口(GET): http://ip.taobao.com/service/getI ...