洛谷P4027,[NOI2007]货币兑换,CDQ分治+斜率优化+维护凸包

正题

      题目直接戳这里

      首先,要贪心一点,我们肯定会选在j天买入i天卖出收益最大的时候整体买入和整体卖出。

      所以很明显就有一个Dp方程。f(i)=max(f(i-1),\max_{j=1}^{x-1}a_i*x_j+b_i*y_j)

      其中ai指的是在这时候a卷的价值,bi同理。

      xj表示在j的时候买的最多的xj的卷数。

      yj同理

      然后遇到这种东西不知道怎么办就只能暴力找。

      n的平方。

      想着怎么优化。

      化简一下。

      洛谷P4027,[NOI2007]货币兑换,CDQ分治+斜率优化+维护凸包_第1张图片

      诶.y=kx+b耶。想使得f(i)最大,那么必须使得b(截距)最大。那么很明显,用前i-1个点在笛卡尔坐标系上描点,用斜率为\frac{-a_i}{b_i}的直线从上往下扫,碰到的第一个点就是能使i获得最大价值的j。明显是个上凸包。但是题目没有保证-\frac{a_i}{b_i}有序,所以不能用单调队列来维护。想想,是不是还有CDQ分治啊。

      所以我们把每一个i按照对应的斜率进行排序。

      每次分的时候,我们把i分成左右两堆,一堆的初始序是小于等于mid,一堆的初始序是大于mid的,那么现在可以保证,左右两堆虽然不是按顺序的,但是去到了自己要去的地方,而且是按斜率排序的。

       每次处理完左边,就要计算一下左边对右边产生的影响。

       用左边构造一个上凸包,又因为右边的斜率是有序的,所以我们可以花(r-l+1)/2的时间扫完整个凸包,并且获得右边一些点的答案(因为有些点的答案不存在于这些点内)。

       处理完左右两边,我们按照x排序,返回,这样又可以使得上一层可以O(n)构造凸包。

       另外x_iy_i是可以通过解一个一元二次方程得到的(当然要知道f(i)才能求)。

       另外,做这种毒瘤题一定要注意精度

#include
#include
#include
#include
#include
#include
#include
using namespace std;

int n;
double eps=1e-8;
double INF=1e20;
struct node{
	double a,b,r,k,x,y;
	int id;
	bool operator<(const node p)const{
		return k>p.k;
	}
}q[100010],t[100010];
double f[100010];
int op[100010];

double get_k(int a,int b){
	if(b==0) return -INF;
	if(fabs(q[a].x-q[b].x)1 && get_k(op[tt-1],op[tt])q[i].k) now++;
		f[q[i].id]=max(f[q[i].id],q[op[now]].x*q[i].a+q[op[now]].y*q[i].b);
	}
	solve(mid+1,r);
	left=l,right=mid+1;
	for(int i=l;i<=r;i++)
		if(((q[left].xr)&&left<=mid)
			t[i]=q[left++];
		else t[i]=q[right++];
	for(int i=l;i<=r;i++) q[i]=t[i];
	return ;
}

int main(){
	scanf("%d %lf",&n,&f[0]);
	for(int i=1;i<=n;i++){
		scanf("%lf %lf %lf",&q[i].a,&q[i].b,&q[i].r);
		q[i].k=-q[i].a/q[i].b;
		q[i].id=i;
	}
	sort(q+1,q+1+n);
	solve(1,n);
	printf("%.3lf",f[n]);
}

      

你可能感兴趣的:(洛谷P4027,[NOI2007]货币兑换,CDQ分治+斜率优化+维护凸包)