1. 程式人生 > >bzoj-1492 貨幣兌換Cash (1)——平衡樹維護凸包

bzoj-1492 貨幣兌換Cash (1)——平衡樹維護凸包

ota scan efi mod 結構 inf borde -a 中序

題意:

有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; }



bzoj-1492 貨幣兌換Cash (1)——平衡樹維護凸包