[BZOJ1492]-[NOI2007]货币兑换Cash-斜率优化+CDQ
2018-02-14 20:24
543 查看
说在前面
自己居然蠢到连凸包都可以写错,没救了…题目
BZOJ1492传送门这题面太长= =
幸好不是权限题,看题可以进传送门
解法
关于dp式子的推导,在me另一篇动态凸包的题解里,附传送门假设已经推出转移式,大概是这样dp[i]=A[i]∗x+B[i]∗ydp[i]=A[i]∗x+B[i]∗y(其中x=Rate[j]∗dp[j]A[j]∗Rate[j]+B[j]x=Rate[j]∗dp[j]A[j]∗Rate[j]+B[j],y=dp[j]A[j]∗Rate[j]+B[j]y=dp[j]A[j]∗Rate[j]+B[j])
这个式子直接上的复杂度是Θ(N2)Θ(N2)的,时间复杂度不能承受,于是考虑优化。发现转移式是一个直线的表达式,于是想到斜率优化。对式子进行移项,得到y=−A[i]B[i]x+dp[i]B[i]y=−A[i]B[i]x+dp[i]B[i],其中,我们希望纵截距尽可能大。显然这样的点(x,y)一定在凸包上。
但是这个式子的x和y都不单调,不能用单调队列或者单调栈进行优化,于是可以采用splay维护动态凸包,每次询问就在凸包里二分找到最优的点。然后将新得到的这个点插入凸包中即可。
当然,还有一种更简单的方法,CDQ分治。因为i只能由1到i-1转移过来,于是可以先递归处理左区间,用左区间更新右区间的答案,然后再递归处理右区间。因为左区间的dp值已经求出了,所以可以作出凸包,而右区间的询问按照斜率从大到小排序,这样就可以用两个指针扫一扫完成更新。
左区间的凸包需要x有序,这一步可以用类似归并的思想,右区间询问的顺序也可以预先跑一遍归并处理出来。这样总时间复杂度是Θ(NlogN)Θ(NlogN)的。(当然也可以在每一层都sort一遍,上界Θ(Nlog2N)Θ(Nlog2N)也可以过)
关于凸包,凸包只要叉乘小于等于0就必须出栈,因为等于0的情况可能是两个点重在一起,如果后面有更优的点,这两个点就无法出栈了
下面是代码
/************************************************************** Problem: 1492 User: Izumihanako Language: C++ Result: Accepted Time:968 ms Memory:16448 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; int N , seq[18][100005] ; double dp[100006] , A[100005] , B[100005] , R[100006] , slo[100005] ; struct Point{ double x , y ; } p[100005] , cvx[100005] , tmp[100005] ; typedef Point Vector ; Vector operator + ( const Vector &A , const Vector &B ){ return ( Vector ){ A.x + B.x , A.y + B.y } ; } Vector operator - ( const Vector &A , const Vector &B ){ return ( Vector ){ A.x - B.x , A.y - B.y } ; } double cross( const Vector &A , const Vector &B ){ return A.x * B.y - A.y * B.x ; } double slope( const Point &A , const Point &B ){ return ( B.y - A.y ) / ( B.x - A.x ) ; } ; void mergeSort( int dep , int lf , int rg ){ if( lf == rg ) return ( void )( seq[dep][lf] = lf ) ; int mid = ( lf + rg ) >> 1 , Lpt = lf , Rpt = mid + 1 , pt = lf ; mergeSort( dep + 1 , lf , mid ) ; mergeSort( dep + 1 , mid+1,rg ) ; while( Lpt <= mid && Rpt <= rg ){ if( slo[ seq[dep+1][Lpt] ] >= slo[ seq[dep+1][Rpt] ] ) seq[dep][pt] = seq[dep+1][Lpt] , Lpt ++ , pt ++ ; else seq[dep][pt] = seq[dep+1][Rpt] , Rpt ++ , pt ++ ; } while( Lpt <= mid ) seq[dep][pt] = seq[dep+1][Lpt] , pt ++ , Lpt ++ ; while( Rpt <= rg ) seq[dep][pt] = seq[dep+1][Rpt] , pt ++ , Rpt ++ ; } void CDQ( int dep , int lf , int rg ){ if( lf == rg ){ dp[lf] = max( dp[lf] , dp[lf-1] ) ; double tmp = dp[lf] / ( A[lf] * R[lf] + B[lf] ) ; p[lf] = ( Point ){ R[lf] * tmp , tmp } ; return ; } int mid = ( lf + rg ) >> 1 , topp = 0 ; CDQ( dep + 1 , lf , mid ) ; // convex for( int i = lf ; i <= mid ; i ++ ){ while( topp > 1 && cross( p[i] - cvx[topp-1] , cvx[topp] - cvx[topp-1] ) <= 0 ) topp -- ; cvx[++topp] = p[i] ; } // update dp[] for( int i = mid + 1 , pt = 1 ; i <= rg ; i ++ ){ int id = seq[dep][i] ; while( pt < topp && slope( cvx[pt] , cvx[pt+1] ) > slo[id] ) pt ++ ; dp[id] = max( dp[id] , A[id] * cvx[pt].x + B[id] * cvx[pt].y ) ; } CDQ( dep + 1 , mid+1,rg ) ; //sort point by x , preparing for upper int Lpt = lf , Rpt = mid + 1 , pt = lf ; while( Lpt <= mid && Rpt <= rg ){ if( p[Lpt].x <= p[Rpt].x ) tmp[pt] = p[Lpt] , pt ++ , Lpt ++ ; else tmp[pt] = p[Rpt] , pt ++ , Rpt ++ ; } while( Lpt <= mid ) tmp[pt] = p[Lpt] , pt ++ , Lpt ++ ; while( Rpt <= rg ) tmp[pt] = p[Rpt] , pt ++ , Rpt ++ ; for( int i = lf ; i <= rg ; i ++ ) p[i] = tmp[i] ; } void solve(){ mergeSort( 0 , 1 , N ) ; CDQ( 1 , 1 , N ) ; printf( "%.3f" , dp ) ; } int main(){ scanf( "%d%lf" , &N , &dp[0] ) ; for( int i = 1 ; i <= N ; i ++ ){ scanf( "%lf%lf%lf" , &A[i] , &B[i] , &R[i] ) ; slo[i] = - A[i] / B[i] ; } solve() ; }
相关文章推荐
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化dp+splay|cdq分治维护凸包)
- [BZOJ1492][NOI2007][CDQ分治][斜率优化][DP]货币兑换Cash
- [BZOJ 1492][NOI2007]货币兑换Cash:CDQ分治|DP斜率优化
- [DP 斜率优化 CDQ分治||动态维护凸包] BZOJ 1492 [NOI2007]货币兑换Cash
- [bzoj1492][cdq分治][斜率优化][NOI2007]货币兑换Cash
- [BZOJ1492][NOI2007]货币兑换Cash && CDQ分治+斜率优化
- BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化
- 【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治
- bzoj [NOI2007]货币兑换Cash (cdq分治+斜率优化 )
- BZOJ 1492 [NOI2007]货币兑换Cash(斜率优化dp+splay维护凸壳)
- bzoj1492 [NOI2007]货币兑换Cash(斜率优化DP+cdq分治)
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
- BZOJ_P1492 [NOI2007]货币兑换Cash(CDQ分治+斜率优化)
- bzoj1492 [NOI2007]货币兑换Cash(斜率优化+CDQ分治)
- NOI 2007 货币兑换Cash (bzoj 1492) - 斜率优化 - 动态规划 - CDQ分治
- 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP
- BZOJ1492:[NOI2007]货币兑换Cash (CDQ分治+斜率优化DP/平衡树维护凸壳)
- BZOJ 1492 货币兑换Cash(CDQ分治+斜率优化dp)
- BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]
- 【bzoj1492】【NOI2007】【货币兑换】【斜率优化+cdq分治】