NOI2007 货币兑换

题目链接:https://www.luogu.com.cn/problem/P4027
典型的线性 d p dp dp
题目最底下居然还有提示
d p [ i ] dp[i] dp[i]在第 i i i天之前买光股票的最大收入
d p [ i ] = m a x ( d p [ j ] ∗ r a t e j r a t e j ∗ a j + b j ∗ a i + d p [ j ] r a t e j ∗ a j + b j ∗ b i ) dp[i]=max(\frac{dp[j]*rate_j}{rate_j*a_j+b_j}*a_i+\frac{dp[j]}{rate_j*a_j+b_j}*b_i) dp[i]=max(ratejaj+bjdp[j]ratejai+ratejaj+bjdp[j]bi)
式子中带乘积,可以考虑斜率优化(很好写成 Y = K x + B Y=Kx+B Y=Kx+B的形式)
令: d p [ j ] ∗ r a t e j r a t e j ∗ a j + b j = − x \frac{dp[j]*rate_j}{rate_j*a_j+b_j}=-x ratejaj+bjdp[j]ratej=x
d p [ j ] r a t e j ∗ a j + b j = Y \frac{dp[j]}{rate_j*a_j+b_j}=Y ratejaj+bjdp[j]=Y
d p [ i ] = B dp[i]=B dp[i]=B
则: Y = a [ i ] b [ i ] x + B Y=\frac{a[i]}{b[i]}x+B Y=b[i]a[i]x+B
就可以斜率优化了
但这道题比较变态
要维护动态凸包
可以用平衡树维护
x x x坐标排序,每次查找最值和动态往里加点
200行fhqtreap

C o d e Code Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5,Mod=1e9+7;
const double esp=1e-6;
struct Point{
	double x,y;
	inline Point(double xx=0,double yy=0){x=xx,y=yy;}
};
inline double dcmp(double);
inline bool operator <= (const Point &X,const Point &Y){return dcmp(X.x-Y.x)<=0;}
inline bool operator > (const Point &X,const Point &Y){return X.x > Y.x;}
struct node{
	int rank,lc,rc,size;
	Point val;
	inline node(){lc=rc=0;}
}tree[MAXN+10];
struct w{double a,b,rate;}a[MAXN+10];
double dp[MAXN+10];
int tot=0;
inline double slope(Point,Point);
int Query(int,double);
void Insert(int&,Point);
namespace fhqtreap{
	inline void update(int);
	inline int add_node(Point);
	void spilt(int,int&,int&,Point);
	void merge(int&,int,int);
	void Insert(int&,Point);
	void Del_all(int&,Point);
	Point get_kth(int,int);
	Point get_pre(int&,Point);
	Point get_nx(int&,Point);
}


int main(){
	//freopen ("std.in","r",stdin);
	//freopen ("std.out","w",stdout);
	srand((unsigned)time(NULL));
	int n,s,rt=0;
	scanf("%d%d",&n,&s);
	for (register int i=1;i<=n;++i)	scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].rate);
	dp[0]=s;
	for (register int i=1;i<=n;++i){
		dp[i]=dp[i-1];
		double k=a[i].a/a[i].b;
		if (rt){
			int j=Query(rt,k);
			Point p=tree[j].val;
			dp[i]=max(dp[i],-p.x*a[i].a+p.y*a[i].b);
		}
		double x=-dp[i]*a[i].rate/(a[i].rate*a[i].a+a[i].b);
		double y=dp[i]/(a[i].rate*a[i].a+a[i].b);
		Insert(rt,Point(x,y));
	}
	printf("%.3f\n",dp[n]);
	return 0;
}

inline double dcmp(double x){return fabs(x)<esp? 0 : x;}
inline double slope(Point x,Point y){return (x.y-y.y)/(x.x-y.x);}

void Insert(int &rt,Point x){
	using namespace fhqtreap;
	if (!rt){
		fhqtreap::Insert(rt,x);
		return;
	}
	Point y=get_pre(rt,x),l;
	Point z=get_nx(rt,x),r;
	if (y.y!=-Mod && z.y!=-Mod && dcmp(slope(y,x)-slope(z,x))<=0)	return;
	while (1){
		if (y.x == -Mod && y.y == -Mod)	break;
		l=get_pre(rt,y);
		if (l.x==-Mod && l.y==-Mod || dcmp(slope(l,y) - slope(y,x)) > 0)	break;
		Del_all(rt,y);
		y=get_pre(rt,x);
	}
	while (1){
		if (z.x == -Mod && z.y == -Mod)	break;
		r=get_nx(rt,z);
		if (r.x==-Mod && r.y==-Mod || dcmp(slope(r,z) - slope(z,x)) < 0)	break;
		Del_all(rt,z);
		z=get_nx(rt,x);
	}
	fhqtreap::Insert(rt,x);
}

int Query(int rt,double k){
	using namespace fhqtreap;
	int root=rt;
	while (1){
		double s1=tree[rt].lc? slope(tree[rt].val,get_pre(root,tree[rt].val)) : Mod;
		double s2=tree[rt].rc? slope(tree[rt].val,get_nx(root,tree[rt].val)) : esp;
		if (dcmp(k-s2) < 0)	rt=tree[rt].rc;
		else    if (dcmp(k-s1) > 0)	rt=tree[rt].lc;
		else    break;
	}
	return rt;
}

namespace fhqtreap{
	inline void update(int rt){tree[rt].size=tree[tree[rt].lc].size+tree[tree[rt].rc].size+1;}
	inline int add_node(Point x){
		int rt=++tot;
		tree[rt].val=x;
		tree[rt].rank=rand()%Mod;
		tree[rt].size=1;
		return rt;
	}
	
	void spilt(int rt,int &rx,int &ry,Point val){
		if (!rt){
			rx=ry=0;
			return;
			}
		if (tree[rt].val <= val)	spilt(tree[rt].rc,tree[rx=rt].rc,ry,val);
		else    spilt(tree[rt].lc,rx,tree[ry=rt].lc,val);
		update(rt);
	}

	void merge(int &rt,int rx,int ry){
		if (!rx || !ry){
			rt=rx+ry;
			return;
		}
		if (tree[rx].rank <= tree[ry].rank)	merge(tree[rt=rx].rc,tree[rx].rc,ry);
		else    merge(tree[rt=ry].lc,rx,tree[ry].lc);
		update(rt);
	}
	
	void Insert(int &rt,Point x){
		int new_node=add_node(x);
		if (!rt){
			rt=new_node;
			return;
		}
		int rx,ry;
		spilt(rt,rx,ry,x);
		merge(rx,rx,new_node);
		merge(rt,rx,ry);
	}
	
	void Del_all(int &rt,Point x){
		int rx,ry,rz;
		spilt(rt,rx,ry,x);
		x.x-=esp*2;
		spilt(rx,rx,rz,x);
		merge(rt,rx,ry);
	}
	
	Point get_kth(int rt,int k){
		int cnt=0;
		while ((cnt=tree[tree[rt].lc].size+1)!=k){
			if (k<cnt)	rt=tree[rt].lc;
			else    k-=cnt,rt=tree[rt].rc;
		}
		return tree[rt].val;
	}
	
	Point get_pre(int &rt,Point x){
		int rx,ry;
		x.x-=esp*2;
		spilt(rt,rx,ry,x);
		if (!tree[rx].size){
			rt=ry;
			return Point(-Mod,-Mod);
		}
		Point k=get_kth(rx,tree[rx].size);
		merge(rt,rx,ry);
		return k;
	}
	
	Point get_nx(int &rt,Point x){
		int rx,ry;
		spilt(rt,rx,ry,x);
		if (!tree[ry].size){
			rt=rx;
			return Point(-Mod,-Mod);
		}
		Point k=get_kth(ry,1);
		merge(rt,rx,ry);
		return k;
	}
}

//dp[i]=max{dp[j]*a[j].rate*a[i].a/(a[j].rate*a[j].a+a[j].b)+d[j]*a[i].b/(a[j].rate*a[j].a+a[j].b)}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值