BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )

dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡树维护上凸壳, 询问时就在凸壳上二分...时间复杂度O(NlogN)

-----------------------------------------------------------------------------------------------

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
 
using namespace std;
 
#define K(a, b) ((a.y - b.y) / (a.x - b.x))
 
const int maxn = 100009;
const double eps = 1e-7;
const double INF = 1e100;
 
double dp, A, B, R, K;
int N;
 
struct P {
double x, y;
P(double _x = 0, double _y = 0) : x(_x), y(_y) {
}
bool operator < (const P &p) const {
return x < p.x;
}
bool operator == (const P &p) const {
return fabs(x - p.x) < eps && fabs(y - p.y) < eps;
}
} p;
 
struct Node {
Node* ch[2];
double lk, rk;
int r;
P p;
} pool[maxn], *pt, *Root, *Null;
 
void Init_Treap() {
pt = pool;
pt->ch[0] = pt->ch[1] = pt;
pt->p = P(-INF, -INF);
Root = Null = pt++;
}
 
void Rotate(Node*&t, int d) {
Node* o = t->ch[d ^ 1];
t->ch[d ^ 1] = o->ch[d];
o->ch[d] = t;
t = o;
}
 
void Insert(Node*&t) {
if(t == Null) {
(t = pt++)->p = p, t->r = rand();
t->ch[0] = t->ch[1] = Null;
} else {
int d = (t->p < p);
Insert(t->ch[d]);
if(t->ch[d]->r > t->r) Rotate(t, d ^ 1);
}
}
 
void Delete(Node*&t) {
int d = (p == t->p ? -1 : (t->p < p));
if(d == -1) {
if(t->ch[0] != Null && t->ch[1] != Null) {
int _d = (t->ch[0]->r > t->ch[1]->r);
Rotate(t, _d), Delete(t->ch[_d]);
} else
t = (t->ch[0] != Null ? t->ch[0] : t->ch[1]);
} else 
Delete(t->ch[d]);
}
 
Node* Pred(P &p) {
Node* ret = Null;
for(Node* o = Root; o != Null; ) if(o->p < p)
ret = o, o = o->ch[1];
else
o = o->ch[0];
return ret;
}
 
Node* Succ(P &p) {
Node* ret = Null;
for(Node* o = Root; o != Null; ) if(p < o->p)
ret = o, o = o->ch[0];
else
o = o->ch[1];
return ret;
}
 
Node* Find(P &p) {
for(Node* t = Root; t != Null; ) {
if(fabs(t->p.x - p.x) < eps) return t;
t = (p.x < t->p.x ? t->ch[0] : t->ch[1]);
}
return 0;
}
 
P Select(Node*&t) {
if(t->r == -1) return Select(t->ch[1]);
if(t->r == -2) return Select(t->ch[0]);
if(K - t->lk < eps && t->rk - K < eps) return t->p;
return K - t->lk > eps ? Select(t->ch[0]) : Select(t->ch[1]);
}
 
void Init() {
Init_Treap();
p = P(0, -INF), Insert(Root);
p = P(1e10, -INF), Insert(Root);
Node* t = pt;
(--t)->r = -2, (--t)->r = -1;
}
 
void Add() {
double b = dp / (A * R + B), a = b * R;
P o = P(a, b);
Node *t = Find(o);
if(t) {
if(t->p.y - o.y > eps) return;
p = t->p, Delete(Root);
}
Node *L = Pred(o), *R = Succ(o);
if(R->p == o || K(o, R->p) - K(o, L->p) > eps) return;
for(Node* LL = Pred(L->p); LL != Null; ) {
if(K(o, L->p) - K(L->p, LL->p) > eps)
p = L->p, Delete(Root);
else
break;
L = LL, LL = Pred(L->p);
}
pt->lk = L->rk = K(L->p, o);
for(Node* RR = Succ(R->p); RR != Null; ) {
if(K(RR->p, R->p) - K(R->p, o) > eps)
p = R->p, Delete(Root);
else
break;
R = RR, RR = Succ(R->p);
}
R->lk = pt->rk = K(R->p, o);
p = o, Insert(Root);
}
 
void Work() {
scanf("%d%lf", &N, &dp);
for(int i = 0; i < N; i++) {
scanf("%lf%lf%lf", &A, &B, &R);
if(i) {
K = -A / B;
P o = Select(Root);
dp = max(dp, A * o.x + B * o.y);
}
Add();
}
printf("%.3lf\n", dp);
}
 
int main() {
Init();
Work();
return 0;
}

-----------------------------------------------------------------------------------------------

1492: [NOI2007]货币兑换Cash

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 2843   Solved: 1201
[ Submit][ Status][ Discuss]

Description

BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )_第1张图片

Input

第一行两个正整数N、S,分别表示小Y 能预知的天数以及初始时拥有的钱数。 接下来N 行,第K 行三个实数AK、BK、RateK,意义如题目中所述

Output

只有一个实数MaxProfit,表示第N 天的操作结束时能够获得的最大的金钱 数目。答案保留3 位小数。

Sample Input

3 100
1 1 1
1 2 2
2 2 3

Sample Output

225.000

HINT

BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )_第2张图片

测试数据设计使得精度误差不会超过10-7。
对于40%的测试数据,满足N ≤ 10;
对于60%的测试数据,满足N ≤ 1 000;
对于100%的测试数据,满足N ≤ 100 000;

Source

 

你可能感兴趣的:(BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 ))