Time Limit:10000MS Memory Limit:65536K Description Input
Output
Sample Input
Sample Output
Hint Source xinyue |
题目:http://mail.bashu.cn:8080/bs_oj/showproblem?problem_id=2238
题意:已知n天里两种债券的价值,还有每天可以买入的比例,求到第n天最多能获得的钱
分析:这题算是个经典题吧,具体的题解到处都是,这里就简单说说。。。
我们很容易想到一个转移方程,f[ i ]为第i天能获得的最多钱数,那么有f[ i ]=max{ f[ j ]/(r[ j ]*a[ j ]+b[ j ])*r[ j ]*a[ i ]+f[ j ]/(r[ j ]*a[ j ]+b[ j ])*b[ i ]} 1<=j<i
这样转移的复杂度为O(n^2)
我们容易联想到斜率优化之类的,那么我们试着假设 x= f[ j ]/(r[ j ]*a[ j ]+b[ j ])*r[ j ], y= f[ j ]/(r[ j ]*a[ j ]+b[ j ]) ,G=a[ i ]*x +b[ i ]*y
那么有 y=-(a[i ]/b[i])*x+G/b[i] ,这个看起来可以用斜率优化来做,但是再仔细一看,x是无序的,y也是无序的,根本没办法用队列来维护一条凸线。。。
这时候splay之类的平衡树就派上用场了,我们可以用splay来维护这条上凸线 ^_^
具体先维护以x 大小为序的splay,对于每个节点,记录离他最近的左端点和右端点(待会维护凸线需要用到)
1.每次插入一个节点,先把它转为根节点,再往右边查找最近的满足凸线性质的节点(也就是 根->x->x最近的右端点向右拐),然后把它转到根节点下面,直接删掉其左子树。
往左边维护类似,由于这个新的节点可能不是凸线上的节点,再把它当前的左儿子转为根,做一次向右维护就行
2.每次查找值的话,先判断计算出离他最近的左右端点的值,往大的那边走就行,这里我遇到了问题,不知道是精度问题还是什么问题,有两组数据死活过不去,我加了一个判断,当前值大于最近的左端点和右端点的值,直接退出查找,去掉这句就AC,删了就wa= =
代码:
#include<cstdio> #include<iostream> using namespace std; const int mm=111111; double f[mm],a[mm],b[mm],r[mm],X[mm],Y[mm],tmp; struct SplayTree { int son[mm][2],far[mm],p[mm],tp[mm][2]; int rt,size; void Link(int x,int y,int c) { far[x]=y,son[y][c]=x; } void Rotate(int x,int c) { int y=far[x]; Link(x,far[y],son[far[y]][1]==y); Link(son[x][!c],y,c); Link(y,x,!c); } void Splay(int x,int g) { for(;far[x]!=g;) { int y=far[x],cx=son[y][1]==x,cy=son[far[y]][1]==y; if(far[y]==g)Rotate(x,cx); else { if(cx==cy)Rotate(y,cy); else Rotate(x,cx); Rotate(x,cy); } } if(!g)rt=x; } void NewNode(int y,int &x,int i) { x=++size; far[x]=y,p[x]=i; tp[x][0]=tp[x][1]=son[x][0]=son[x][1]=0; } void Insert(int i) { int x=rt,y,f; while(son[x][f=(X[p[x]]<X[i])])x=son[x][f]; NewNode(x,son[x][f],i); y=tp[size][f]=tp[x][f]; tp[y][!f]=tp[x][f]=size; tp[size][!f]=x; Splay(size,0); Maintain(); } void Prepare() { NewNode(size=0,rt,1); } bool TurnRight(int a,int b,int c) { if(!a||!c)return 1; return (X[a]-X[b])*(Y[c]-Y[b])>(Y[a]-Y[b])*(X[c]-X[b]); } void Right() { int x=son[rt][1],y=rt; while(x) { if(TurnRight(p[rt],p[x],p[tp[x][1]]))y=x,x=son[x][0]; else x=son[x][1]; } if(y!=rt) { Splay(y,rt); son[y][0]=0; tp[rt][1]=y; tp[y][0]=rt; } } void Left() { int x=son[rt][0],y=rt; while(x) { if(TurnRight(p[tp[x][0]],p[x],p[rt]))y=x,x=son[x][1]; else x=son[x][0]; } if(y!=rt) { Splay(y,rt); son[y][1]=0; tp[rt][0]=y; tp[y][1]=rt; } } void Maintain() { if(son[rt][1])Right(); if(son[rt][0]) { Left(); Splay(son[rt][0],0); Right(); } } double Get(int i,int j) { return X[p[j]]*a[i]+Y[p[j]]*b[i]; } double Find(int i) { int x=rt; double ret=Get(i,x),tmp1,tmp2; while(x) { x=son[x][Get(i,tp[x][0])<Get(i,tp[x][1])]; ret=max(ret,Get(i,x)); } if(x)Splay(x,0); return ret; } }spt; double get() { char c; while((c=getchar())<'0'||c>'9'); double a=c-'0',b=1; while((c=getchar())>='0'&&c<='9')a=a*10+c-'0'; if(c=='.') while((c=getchar())>='0'&&c<='9')b*=10.0,a=a+(c-'0')/b; return a; } int i,n; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); while(~scanf("%d%lf",&n,&f[1])) { for(i=1;i<=n;++i) a[i]=get(),b[i]=get(),r[i]=get(); Y[1]=f[1]/(r[1]*a[1]+b[1]); X[1]=Y[1]*r[1]; spt.Prepare(); for(i=2;i<=n;++i) { f[i]=spt.Find(i); f[i]=max(f[i],f[i-1]); Y[i]=f[i]/(r[i]*a[i]+b[i]); X[i]=Y[i]*r[i]; spt.Insert(i); } printf("%.3lf\n",f[n]); } return 0; }