货币兑换Cash

http://www.lydsy.com/JudgeOnline/problem.php?id=1492

这道题的关键在于 一天可以交易任意次和

必然存在一种最优的买卖方案满足:
·每次买进操作使用完所有的人民币;
·每次卖出操作卖出所有的金券。

这样就可以用一维的DP求解了,设dp[i]表示前i天所得的最优解

由于只能按比例购买,所以可以把A和B组合成一种价格为K = (a[j]*r[j]+b[j])/(r[j]+1)的证券

则dp[i] = max(dp[j]/K*(r[j]/(r[j]+1))*a[i]+dp[j]/K*(1/(r[j]+1))*b[i])

化简后 dp[i] = max(dp[j]/(a[j]*r[j]+b[j])*r[j]*a[i]+dp[j]/(a[j]r[j]+b[j])*b[i])

令X表示dp[j]/(a[j]*r[j]+b[j])*r[j],Y表示dp[j]/(a[j]r[j]+b[j])

G = X*a[i]+Y*b[i]  => Y = -(a[i]/b[i])X+G/b[i]

但是X,Y,斜率全都不是单调的了,所以不能用单调队列维护,但是其本质还是维护一个单调递减的上凸壳,只是要从中间插入点并且维护,查询要找到与直线相切的点。

插入点并且维护论文和网上都有很多详细的说明了,这里说一下查询的问题

论文上说用二分,网上的很多题解也是在splay上进行二分找到相切点,我开始也这样写,但发现有俩个点过不了,然后一细想,这个不是单调函数,而是单峰函数,需要三分才行,然后就写了一个splay上的三分,过了那俩个点,但效率剧慢,因为splay上的三分需要伸展多次,很耗时间,后来又想到了一种方法不用伸展的方法

beg 先判断左右孩子的值,

如果发现有一个比根节点大,则移动到相应的孩子(因为函数是单峰的,所以俩个孩子中最多只能有一个比根节点大),

跳转beg

否则判断该根节点的前驱和后继的值,如果发现比根节点大的,移动到相应的孩子(前驱对应左孩子,后继对应右孩子)

跳转beg

如果还没发现比根节点大就return,(这说明已经到达了函数的峰值)


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string>    
#include <sstream>
#include <utility>   
#include <ctime>
#include <bitset>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::stringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;
using std::deque;
using std::unique;
using std::lower_bound;
using std::random_shuffle;
using std::bitset;
using std::upper_bound;
using std::multiset;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;
typedef LL TY;
typedef long double LF;

const int MAXN(2000010);
const int MAXM(101010);
const int MAXE(100010);
const int MAXK(6);
const int HSIZE(31313);
const int SIGMA_SIZE(26);
const int MAXH(19);
const int INFI((INT_MAX-1) >> 1);
const ULL BASE(31);
const LL LIM(10000000);
const int INV(-10000);
const int MOD(20100403);
const double EPS(1e-6);
const LF PI(acos(-1.0));

template<typename T> void checkmax(T &a, T b){if(b > a) a = b;}
template<typename T> void checkmin(T &a, T b){if(b < a) a = b;}
template<typename T> T ABS(const T &a){return a < 0? -a: a;}

double table[MAXN];
double A[MAXN], B[MAXN], R[MAXN];

struct SPLAY_TREE
{
	struct NODE
	{
		double x, y;
		int size;
		NODE *fa, *ch[2];
	};
	NODE pool[MAXN];
	NODE *root, *NIL, *rear, *ll, *rl;
	inline void push_up(NODE *rt){rt->size = rt->ch[0]->size+rt->ch[1]->size+1;}
	void newnode(NODE *&rt, NODE *f, double x_, double y_)
	{
		rt = rear++;
		rt->x = x_;
		rt->y = y_;
		rt->size = 1;
		rt->fa = f;
		rt->ch[0] = rt->ch[1] = NIL;
	}
	void init()
	{
		NIL = pool;
		NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;
		NIL->size = 0;
		rear = pool+1;
		newnode(root, NIL, -1e13, 1e6);
		newnode(root->ch[1], root, 1e6, -1e13);
		push_up(root->ch[1]);
		push_up(root);
		ll = root;
		rl = root->ch[1];
	}

	void rotate(NODE *rt, int flag)
	{
		NODE *f = rt->fa;
		f->ch[!flag] = rt->ch[flag];
		rt->ch[flag]->fa = f;
		rt->fa = f->fa;
		if(f->fa != NIL) f->fa->ch[f->fa->ch[1] == f] = rt;
		rt->ch[flag] = f;
		f->fa = rt;
		push_up(f);
	}

	void splay(NODE *rt, NODE *goal)
	{
		while(rt->fa != goal)
		{
			if(rt->fa->fa == goal)
				rotate(rt, rt->fa->ch[0] == rt);
			else
			{
				NODE *f = rt->fa;
				int flag = (f->fa->ch[0] == f);
				if(f->ch[flag] == rt) rotate(rt, !flag);
				else rotate(f, flag);
				rotate(rt, flag);
			}
		}
		push_up(rt);
		if(goal == NIL) root = rt;
	}
	NODE *select(NODE *rt, int r)
	{
		while(true)
		{
			if(r == rt->ch[0]->size+1) break;
			if(r <= rt->ch[0]->size) rt = rt->ch[0];
			else 
			{
				r -= rt->ch[0]->size+1;
				rt = rt->ch[1];
			}
		}
		return rt;
	}
	void insert(double x_, double y_)
	{
		NODE *f = NIL, *rt = root;
		//插入(x_, y_)
		while(rt != NIL)
		{
			f = rt;
			rt = rt->ch[rt->x < x_];
		}
		splay(f, NIL);
		if(f->x < x_)
		{
			rt = select(root->ch[1], 1);
			splay(rt, root);
		}
		else
		{
			rt = select(root, root->ch[0]->size);
			splay(rt, NIL);
			splay(f, root);
		}
		newnode(root->ch[1]->ch[0], root->ch[1], x_, y_);
		push_up(root->ch[1]);
		push_up(root);
		splay(root->ch[1]->ch[0], NIL);
		//插入结束
		NODE *tp = root, *tp1, *tp2;
		//向左维护
		tp1 = select(root, root->ch[0]->size);
		splay(tp1, NIL);
		while(root->ch[0]->size >= 1)
		{	
			tp2 = select(root, root->ch[0]->size);
			splay(tp2, NIL);
			if((tp->y-tp1->y)*(tp->x-tp2->x) < (tp->y-tp2->y)*(tp->x-tp1->x)) break;
			tp1 = tp2;
		}
		splay(tp1, NIL);
		splay(tp, root);
		tp->ch[0] = NIL;
		push_up(root->ch[1]);
		push_up(root);
		//向右维护
		splay(tp, NIL);
		tp1 = select(root->ch[1], 1);
		splay(tp1, NIL);
		while(root->ch[1]->size >= 1)
		{
			tp2 = select(root->ch[1], 1);
			splay(tp2, NIL);
			if((tp1->y-tp->y)*(tp2->x-tp->x) > (tp2->y-tp->y)*(tp1->x-tp->x)) break;
			tp1 = tp2;
		}
		splay(tp, NIL);
		splay(tp1, root);
		tp1->ch[0] = NIL;
		push_up(root->ch[1]);
		push_up(root);
		//看(x_, y_)是否在凸壳上
		splay(tp, NIL);
		tp1 = select(root, root->ch[0]->size);
		tp2 = select(root->ch[1], 1);
		if((tp->y-tp1->y)*(tp2->x-tp->x) <= (tp2->y-tp->y)*(tp->x-tp1->x))
		{
			splay(tp1, NIL);
			splay(tp2, root);
			tp2->ch[0] = NIL;
			push_up(root->ch[1]);
			push_up(root);
		}
	}
	double query(int ind)
	{
		double ta = A[ind], tb = B[ind];
		NODE *tp, *f, *tp1, *tp2;
		double t, t1, t2, ret = 0;
		ret = root->x*ta+root->y*tb;
		tp = root;
		while(true)
		{
			f = tp;
			t = tp->x*ta+tp->y*tb;
			t1 = tp->ch[0]->x*ta+tp->ch[0]->y*tb;
			t2 = tp->ch[1]->x*ta+tp->ch[1]->y*tb;
			if(t1 > t)
			{
				checkmax(ret, t1);
				tp = tp->ch[0];
				continue;
			}
			if(t2 > t)
			{
				checkmax(ret, t2);
				tp = tp->ch[1];
				continue;
			}
			if(tp->ch[0] != NIL)
			{
				tp1 = select(tp, tp->ch[0]->size);
				t1 = tp1->x*ta+tp1->y*tb;
				if(t1 > t)
				{
					tp = tp->ch[0];
					continue;
				}
			}
			if(tp->ch[1] != NIL)
			{
				tp2 = select(tp->ch[1], 1);
				t1 = tp2->x*ta+tp2->y*tb;
				if(t1 > t)
				{
					tp = tp->ch[1];
					continue;
				}
			}
			break;
		}
/*      三分法
		splay(ll, NIL);
		splay(rl, root);
		double ret1;
		while(true)                      
		{
			tp = root->ch[1]->ch[0];
			if(tp->size == 1)
			{
				checkmax(ret1, tp->x*ta+tp->y*tb);
				break;
			}
			if(tp->size == 0) break;
			if(tp->size == 2)
			{
				tp1 = select(tp, 1);
				tp2 = select(tp, 2);
			}
			else
			{
				tp1 = select(tp, tp->size/3);
				tp2 = select(tp, tp->size/3*2);
			}
			t1 = tp1->x*ta+tp1->y*tb;
			t2 = tp2->x*ta+tp2->y*tb;
			if(t1 < t2)
			{
				checkmax(ret1, t2);
				tp2 = root->ch[1];
				splay(tp1, NIL);
				splay(tp2, root);
			}
			else
			{
				checkmax(ret1, t1);
				splay(tp2, root);
			}
		}
		if(ABS(ret-ret1) > EPS) throw "w";*/
		return ret;
	}
} spt;

double Y(int ind){ return table[ind]/(A[ind]*R[ind]+B[ind]);}
double X(int ind){ return table[ind]/(A[ind]*R[ind]+B[ind])*R[ind];}

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 main()
{
	int n, s;
//	freopen("d:\\in.txt", "r", stdin);
	while(~scanf("%d%d", &n, &s))
	{
		spt.init();
		for(int i = 1; i <= n; ++i)
		{
			A[i] = get();
			B[i] = get();
			R[i] = get();
		}
		table[1] = s;
		spt.insert(X(1), Y(1));
		for(int i = 2; i <= n; ++i)
		{
			table[i] = max(table[i-1], spt.query(i));
			spt.insert(X(i), Y(i));
		}
		printf("%.3f\n", table[n]);
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值