bzoj-1492 货币兑换Cash (1)——平衡树维护凸包

原创 2015年07月11日 09:37:45

题意:

有n天和m的初始金钱,用来购买AB两种纪念券;

n天里每天都有AB的价格,每天可以进行这样的操作;

1.卖出手中x%的纪念券(AB分别都卖出x%);

2.用x的金钱买入纪念券,买入AB券的比例在第i天为Rate i;

求n天过去之后所获得的最大收益;

金钱和券数均为实数;

n<=100 000;


题解:

首先,虽然题中的买入和卖出都是任意数量的,但是同样的纪念券,分几天卖出得到的收 益,一定小于等于直接在一天卖出的收益;

同样,分几天买入也是不如一天花所有钱买入的;

令:

f[i]为第i天的最大收益;

X[i]为第i天将所有钱数都买入得到的A券数;

Y[i]为第i天将所有钱数都买入得到的B券数;

显然X,Y都可以由f[i]得来;

那么转移方程就是:

f[i]=max(f[i-1],A[i] * X[j] + B[i] * Y[j]);(1<=j< i);

这样转移是O(n^2)的,所以要对后面枚举j的部分优化;

将f[i]=A[i] * X[j] + B[i] * Y[j]整理;

得到Y[i]=(-A[i]/B[i]) * X[i] + f[i]/B[i];

这是一个直线方程的形式,而对于固定的i,直线斜率不变,而要最大化截距;

倘若将所有的[1,i-1]的点计算出(x,y)放在坐标系上;

找到最优值相当于在这个上凸包上找到某个点,使截距最大;

那么这个点左面的斜率一定大于当前i的斜率,右面的斜率一定小于当前i的斜率;

所以这其实就是一个斜率优化的形式;

通常我们做斜率优化都是找到不符合要求的点直接干掉就好的;

因为下一个i的斜率不是递增就是递减;

但是这个-A[i]/B[i]不单调,所以不能O(n)的维护队列处理凸包;

10^5的复杂度是支持O(nlogn)的,所以为了维护凸包可以选择一些数据结构;

那么就维护一个Splay,每个结点都在凸包上,中序遍历就是按x递增同时也按斜率递减的序列;

如果把Splay看做logn,那么每个点最多进出凸包一次,二分查询斜率共n次;

复杂度O(nlogn)还是听起来很好的;

但是写起来一点也不好玩!

总之维护凸包时对Splay中点较少时的讨论很烦。。。

照着对拍调数据改改也就过了;

代码3k+,时间960ms,这个跑的感觉也是挺快的了;

下一篇写写更神的CDQ分治,毕竟数据结构对代码能力要求颇高;

7/17

我被D了。。。斜率打成了shope,恩应该是slope无误;


代码:


#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define which(x)	(tr[tr[x].fa].ch[1]==x)
const double INF = 1e100;
const double EPS = 1e-8;
using namespace std;
struct Point
{
	double x, y, s1, s2;
	int fa, ch[2];
}tr[N];
int root, tot;
double f[N], A[N], B[N], R[N], X[N], Y[N];
void get_slope(int a, int b)
{
	if (!a)			tr[b].s1 = INF;
	else if (!b)		tr[a].s2 = -INF;
	else
	{
		if (fabs(tr[a].x - tr[b].x)<EPS)
			tr[a].s2 = tr[b].s1 = (tr[a].y<tr[b].y ? INF : -INF);
		else
			tr[a].s2 = tr[b].s1 = (tr[a].y - tr[b].y) / (tr[a].x - tr[b].x);
	}
}
void Rotate(int x)
{
	int f = tr[x].fa;
	if (!f)	return;
	bool k = which(x);
	tr[f].ch[k] = tr[x].ch[!k];
	tr[x].ch[!k] = f;
	tr[tr[f].fa].ch[which(f)] = x;
	tr[x].fa = tr[f].fa;
	tr[tr[f].ch[k]].fa = f;
	tr[f].fa = x;
}
void Splay(int x, int g)
{
	if (!x)	return;
	while (tr[x].fa != g)
	{
		int f = tr[x].fa;
		if (tr[f].fa == g)
		{
			Rotate(x);
			break;
		}
		if (which(x) ^ which(f))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
	if (!g)	root = x;
}
int Pre(int x)
{
	if (!x)	return 0;
	int p = tr[x].ch[0];
	if (!p)	return 0;
	while (tr[p].ch[1])
		p = tr[p].ch[1];
	return p;
}
int Sub(int x)
{
	if (!x)	return 0;
	int p = tr[x].ch[1];
	if (!p)	return 0;
	while (tr[p].ch[0])
		p = tr[p].ch[0];
	return p;
}
int find(int p, double x)
{
	if (!p)	return 0;
	if (x<tr[p].x)
		return find(tr[p].ch[0], x);
	else
	{
		int t = find(tr[p].ch[1], x);
		return tr[p].x>tr[t].x ? p : t;
	}
}
void Insert(double X, double Y, int no)
{
	int x = find(root, X), y = 0;
	if (!x)
	{
		x = root;
		while (tr[x].ch[0])
			x = tr[x].ch[0];
		Splay(x, 0);
		y = x, x = 0;
	}
	else
	{
		Splay(x, 0);
		Splay(y = Sub(x), x);
	}
	tr[no].x = X, tr[no].y = Y;
	if (y)	tr[no].fa = y, tr[y].ch[0] = no;
	else	tr[no].fa = x, tr[x].ch[1] = no;
	get_slope(x, no);
	get_slope(no, y);
	if (tr[no].s1 <= tr[no].s2)
	{
		tr[y].ch[0] = 0;
		get_slope(x, y);
		return;
	}
	Rotate(no), Rotate(no);
	root = no;
	x = tr[no].ch[0];
	while (tr[x].s1 <= tr[x].s2&&x)
	{
		y = Pre(x);
		Splay(y, x);
		tr[y].fa = no;
		tr[no].ch[0] = y;
		get_slope(y, no);
		x = y;
	}
	x = tr[no].ch[1];
	while (tr[x].s1 <= tr[x].s2&&x)
	{
		y = Sub(x);
		Splay(y, x);
		tr[y].fa = no;
		tr[no].ch[1] = y;
		get_slope(no, y);
		x = y;
	}
}
int query(double S)
{
	int p = root;
	while (S>tr[p].s1 || S<tr[p].s2)
	{
		if (S>tr[p].s1)	p = tr[p].ch[0];
		else			p = tr[p].ch[1];
	}
	return p;
}
int main()
{
	int n, i, j, k;
	scanf("%d%lf", &n, &f[1]);
	for (i = 1; i <= n; i++)
		scanf("%lf%lf%lf", A + i, B + i, R + i);
	tr[0].x = tr[0].y = -INF;
	Y[1] = f[1] / (A[1] * R[1] + B[1]);
	X[1] = R[1] * Y[1];
	Insert(X[1], Y[1], 1);
	for (i = 2; i <= n; i++)
	{
		j = query(-A[i] / B[i]);
		f[i] = max(f[i - 1], A[i] * X[j] + B[i] * Y[j]);
		Y[i] = f[i] / (A[i] * R[i] + B[i]);
		X[i] = R[i] * Y[i];
		Insert(X[i], Y[i], i);
	}
	printf("%.3lf", f[n]);
	return 0;
}


bzoj2388【分块+凸包二分】

凸包写挂了调了好久qaq 忘了凸包上的点的横坐标并不是等距的qaq 首先分块,维护前缀和数组,每块维护一个凸包,那么每一块中的答案都在凸包上可以二分求出 对于区间操作,实际上相当于在区间内加一个...

【bzoj4570】[Scoi2016]妖怪 凸包

二分竟然过不去,真可恶。 二分答案,判断每个点是否可行,转换成解二次不等式,在数轴上求交集。 TLE 正解是凸包。 n个点(x,y),在点(x,y)处的斜率为k(k 单独使点(x,y)最小,得到的答案...

BZOJ 1492 货币兑换 Cash 平衡树维护凸包 CDQ分治

题目大意: 这题真是为难了我1B。。。做了三天,两种方法都试过了一遍,真是膜拜NOI的神犇们能在一个多小时内干掉这道题 首先这题一看就是斜率优化 连递推式都是P=A[i]*X[i]+B...
  • PoPoQQQ
  • PoPoQQQ
  • 2014年08月27日 18:46
  • 2987

BZOJ 2300 HAOI2011 防线修建 平衡树维护凸包

题目大意:给定初始三个点(0,0)(n,0)和(x,y),以及若干其它点,q次询问,每次删除一个点或求一次上凸包长度 平衡树维护凸包……和cash同样的思路为何我时隔2个月后的代码整整短了150行…...
  • PoPoQQQ
  • PoPoQQQ
  • 2014年11月02日 15:56
  • 1548

wc2014 紫荆花之恋

替罪羊树套treap,动态点分治维护 我去,分治树连接出向下的边没改。。。。调了我3个小时,真是醉飞了。。。。。 #include #include #include #include #...

BZOJ 3196 二逼平衡树 树套树

题目大意:。。。BZOJ挂了自己看去 好吧既然BZOJ挂了我还是贴上来吧0.0 破服务器 维护一种数据结构,提供下列操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一...
  • PoPoQQQ
  • PoPoQQQ
  • 2014年09月23日 16:52
  • 1841

BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包
  • wzq_QwQ
  • wzq_QwQ
  • 2015年07月21日 08:06
  • 956

CDQ分治维护凸包 优化dp 【NOI2007】货币兑换cash bzoj1492

题目描述: 小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪 念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有 一个自己的帐户。...
  • Todobe
  • Todobe
  • 2017年03月15日 23:27
  • 175

[DP 斜率优化 CDQ分治||动态维护凸包] BZOJ 1492 [NOI2007]货币兑换Cash

打了个set维护凸包 cdq等待填坑 #include #include #include #include #include #include #define dprintf(...) f...

[BZOJ1492][NOI2007][斜率优化][动态凸包][DP][分治]货币兑换cash

[题目] [算法] dp
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bzoj-1492 货币兑换Cash (1)——平衡树维护凸包
举报原因:
原因补充:

(最多只允许输入30个字)