【bzoj1492】[NOI2007]货币兑换Cash

26 篇文章 0 订阅
6 篇文章 0 订阅

此题非常的经典(似乎cdq分治就是从这题引出的)

基础思想就是cdq分治优化dp。

网上题解似乎很多。。

所以dp什么的就不写了。

重点就是通过cdq来满足斜率优化的条件(维护凸包)

#include <bits/stdc++.h>
#define gc getchar()
#define N 100009
#define mid (l+r>>1)
#define eps 1e-7
#define inf 1e10
using namespace std;
int n,s,sta[N];
double dp[N];
struct node
{
	double k,a,b,r;
	int pos;
	bool operator <(const node &rhs) const
	{
		return k<rhs.k;
	}
}q[N],now[N];
struct point
{
	double x,y;
	bool operator <(const point &rhs) const
	{
		return x<rhs.x||(fabs(x-rhs.x)<eps&&y<rhs.y);
	}
}p[N],Now[N];
double get_k(int a,int b)
{
	if (a==0||b==0) return -inf;
	if (fabs(p[a].x-p[b].x)<eps) return -inf;
	return (p[a].y-p[b].y)/(p[a].x-p[b].x);
}
void solve(int l,int r)
{
	if (l==r)
	{
		dp[l]=max(dp[l-1],dp[l]);
		p[l].y=dp[l]/(q[l].a*q[l].r+q[l].b);
		p[l].x=p[l].y*q[l].r;
		return;
	}
	int l1=l,l2=mid+1;
	for (int i=l;i<=r;i++)
		if (q[i].pos<=mid) now[l1++]=q[i];
		else now[l2++]=q[i];
	for (int i=l;i<=r;i++) q[i]=now[i];
	solve(l,mid);
	int top=0;
	for (int i=l;i<=mid;i++)//k dijian
	{
		while (top>=2&&get_k(i,sta[top])>get_k(sta[top],sta[top-1])) top--;
		sta[++top]=i;
	}
	int j=1;
	for (int i=r;i>mid;i--)//k dijian
	{
		while (j<top&&q[i].k<get_k(sta[j],sta[j+1])) j++;
		dp[q[i].pos]=max(dp[q[i].pos],q[i].a*p[sta[j]].x+q[i].b*p[sta[j]].y);
	}
	solve(mid+1,r);
	l1=l,l2=mid+1;
	for (int i=l;i<=r;i++)
		if ((p[l1]<p[l2]||l2>r)&&l1<=mid) Now[i]=p[l1++];
		else Now[i]=p[l2++];
	for (int i=l;i<=r;i++)
		p[i]=Now[i];
}
int main()
{
	scanf("%d%lf",&n,&dp[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].pos=i;
	}
	sort(q+1,q+n+1);
	solve(1,n);
	printf("%.3lf\n",dp[n]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值