NOI2007 货币兑换
【问题描述】
小 Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和B纪念券(以下简称B券)。每个持有金券的顾客都有一个自己的 帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第K天中A券 和B券的价值分别为AK和BK(元/单位金券)。
为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。
比例交易法分为两个方面:
a)卖出金券:顾客提供一个[0,100]内的实数OP作为卖出比例,其意义为:将OP%的A券和OP%的B券以当时的价值兑换为人民币;
b)买入金券:顾客支付IP元人民币,交易所将会兑换给用户总价值为IP的金券,并且,满足提供给顾客的A券和B券的比例在第K天恰好为RateK;
例如,假定接下来3天内的Ak、Bk、RateK的变化分别为:
时间 | Ak | Bk | Ratek |
第一天 | 1 | 1 | 1 |
第二天 | 1 | 2 | 2 |
第三天 | 2 | 2 | 3 |
假定在第一天时,用户手中有100元人民币但是没有任何金券。
用户可以执行以下的操作:
时间 | 用户操作 | 人民币(元) | A券的数量 | B券的数量 |
开户 | 无 | 100 | 0 | 0 |
第一天 | 买入100元 | 0 | 50 | 50 |
第二天 | 卖出50% | 75 | 25 | 25 |
第二天 | 买入60元 | 15 | 55 | 40 |
第三天 | 卖出100% | 205 | 0 | 0 |
注意到,同一天内可以进行多次操作。
小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能够获得多少元钱。
【输入格式】
第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、BK、RateK,意义如题目中所述。
【输出格式】
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
-----------------------------------------------------------
正解=动归+平衡树
考虑简单Dp
状态:设f[i]为第i天能赚的最多钱为多少
方程:f[i]=max(f[i-1],na*A+nb*B)(j< i)
(na,nb为第j天能换到的A劵数量和B劵数量)
考虑优化:
设 na 为 x,nb 为 y
则sum=x*A+y*B 既 y = -A/B*x+sum/B
由于A,B为定值
sum的Max以 -A/B 为斜率的过点(x,y)的截距的Max*B
显然可能得到Max解截距的点必然是个上凸壳
建立以x为关键字的平衡树维护值(其实很简(dan)单(teng))
由于在树上的点映射的截距具有单调性,
找最大值是便可通过平衡树的前驱后继判断Max在左子树或右子树;
代码如下:
1 #include<cstring> 2 #include<algorithm> 3 #include<cstdio> 4 #include<string> 5 #include<iostream> 6 #include<queue> 7 #define INF 99999999 8 #define LL long long 9 #define Cint(o) const int o=0 10 #define Cdou(o) const double o=0 11 #define Min(num1,num2) if(num1>num2) num1=num2 12 #define Max(num1,num2) if(num1<num2) num1=num2 13 struct Tree{ 14 int l,r,f; 15 double x,y; 16 Tree(Cint(a1),Cint(a2),Cint(a3),Cdou(a4),Cdou(a5)): 17 l(a1),r(a2),f(a3),x(a4),y(a5){} 18 }a[100010]; 19 int Root,Total,n; 20 const long double O=1e-8; 21 void cal(double sum,double A,double B,double K,double &na,double &nb){ 22 nb=sum/(A*K+B); 23 na=nb*K; 24 } 25 void rig(int now){ 26 int f=a[now].f; 27 a[now].f=a[f].f; 28 if(a[a[f].f].l==f) a[a[f].f].l=now; 29 if(a[a[f].f].r==f) a[a[f].f].r=now; 30 a[f].f=now; 31 a[f].l=a[now].r; 32 a[a[now].r].f=f; 33 a[now].r=f; 34 } 35 36 void lef(int now){ 37 int f=a[now].f; 38 a[now].f=a[f].f; 39 if(a[a[f].f].l==f) a[a[f].f].l=now; 40 if(a[a[f].f].r==f) a[a[f].f].r=now; 41 a[f].f=now; 42 a[f].r=a[now].l; 43 a[a[now].l].f=f; 44 a[now].l=f; 45 } 46 double cross(Tree A,Tree B,Tree C){ 47 return (B.x-A.x)*(C.y-A.y)-(C.x-A.x)*(B.y-A.y); 48 // x1 *y2-x2*y1 49 } 50 void splay(int now,int F=0){ 51 while(a[now].f!=F){ 52 int f=a[now].f,ff=a[f].f; 53 if(ff==F) 54 if(a[f].l==now) rig(now); 55 else lef(now); 56 else 57 if(a[ff].l==f) 58 if(a[f].l==now) rig(f),rig(now); 59 else lef(now),rig(now); 60 else 61 if(a[f].l==now) rig(now),lef(now); 62 else lef(f),lef(now); 63 } 64 if(!F) Root=now; 65 } 66 67 void creat(){ 68 Total=2; 69 Root=1; 70 a[1].r=2; 71 a[2].f=1; 72 a[1].x=-INF; 73 a[2].x=INF; 74 } 75 int prev(int now){ 76 splay(now); 77 now=a[now].l; 78 while(a[now].r) now=a[now].r; 79 return now; 80 } 81 int succ(int now){ 82 splay(now); 83 now=a[now].r; 84 while(a[now].l) now=a[now].l; 85 return now; 86 } 87 void del(int start,int now,int end){ 88 splay(start); 89 splay(end,start); 90 a[a[Root].r].l=0; 91 //printf("Delte(%d)",now); 92 } 93 void maintain(int now){ 94 int p=prev(now),s=succ(now); 95 if(p!=1&&s!=2) 96 if(cross(a[p],a[s],a[now])<O){ 97 del(p,now,s); 98 return ; 99 } 100 while(1){ 101 if(p==1) break; 102 int pp=prev(p); 103 if(pp!=1&&cross(a[pp],a[now],a[p])<O) 104 del(pp,p,now), 105 p=pp; 106 else 107 break; 108 } 109 while(1){ 110 if(s==2) break; 111 int ss=succ(s); 112 if(ss!=2&&cross(a[now],a[ss],a[s])<O) 113 del(now,s,ss), 114 s=ss; 115 else 116 break; 117 } 118 } 119 void insert(double x,double y){ 120 for(int now=Root;;){ 121 if(a[now].x-x<O&&x-a[now].x<O){ 122 Max(a[now].y,y); 123 maintain(now); 124 return ; 125 } 126 if(a[now].x-x>O) 127 if(a[now].l) 128 now=a[now].l; 129 else { 130 a[now].l=++Total; 131 a[Total].f=now; 132 a[Total].x=x; 133 a[Total].y=y; 134 maintain(Total); 135 return ; 136 } 137 else 138 if(a[now].r) 139 now=a[now].r; 140 else{ 141 a[now].r=++Total; 142 a[Total].f=now; 143 a[Total].x=x; 144 a[Total].y=y; 145 maintain(Total); 146 return ; 147 148 } 149 } 150 } 151 double fo(Tree now,double K){ 152 return now.y-now.x*K; 153 } 154 int pre(int now){ 155 //splay(now); 156 now=a[now].l; 157 while(a[now].r) now=a[now].r; 158 return now; 159 } 160 161 int suc(int now){ 162 // splay(now); 163 now=a[now].r; 164 while(a[now].l) now=a[now].l; 165 return now; 166 } 167 double slove(double K){ 168 for(int now=Root;;){ 169 if(now==1){ 170 now=suc(now); 171 continue ; 172 } 173 if(now==2){ 174 now=pre(now); 175 continue ; 176 } 177 178 if(a[now].l){ 179 int k=pre(now); 180 if(k!=1&&fo(a[now],K)<fo(a[k],K)+O){ 181 now=a[now].l; 182 continue ; 183 } 184 } 185 if(a[now].r){ 186 int k=suc(now); 187 if(k!=2&&fo(a[now],K)<fo(a[k],K)+O){ 188 now=a[now].r; 189 continue ; 190 } 191 } 192 return fo(a[now],K); 193 194 } 195 } 196 int main(){ 197 double A,B,K,na,nb,s; 198 scanf("%d%lf",&n,&s); 199 scanf("%lf%lf%lf",&A,&B,&K); 200 creat(); 201 cal(s,A,B,K,na,nb); 202 insert(na,nb); 203 for(int i=1;i<n;i++){ 204 scanf("%lf%lf%lf",&A,&B,&K); 205 double temp=slove(-A/B)*B; 206 Max(s,temp); 207 cal(s,A,B,K,na,nb); 208 insert(na,nb); 209 } 210 printf("%.3lf",s); 211 }