cdq分治入门--BZOJ1492: [NOI2007]货币兑换Cash

n<=100000天,一开始有s块钱,每天股票A价格ai,B价格bi,每天可以做的事情:卖出股票;按A:B=RTi的比例买入股票。问最后的最大收益。股票可以为浮点数,答案保留三位。

用脚指头想想就知道是:某一天全部买进来,某一天全部卖出去,没有说买一半卖一半的。

那就可以dp了,f(i)表示前i天最大收益,,其中Xi表示用f(i)块钱在第i天能买多少A券,Yi表示f(i)块前第i天买多少B券,可以自己算,n方过不了。

现要找max(Ai*Xj+Bi*Yj),考虑两个状态j,k,j比k优时

整理得,前提是Xj-Xk

那就平衡树维护一下一个递增的(Yj-Yk)/(Xj-Xk),即维护一个凸包即可,难写,略。

cdq就是这样把一个在线的东西强行转化成离线。

solve(l,r)表示把这个区间里的f算完,solve(l,mid)之后,用(l,mid)的状态更新(mid+1,r)的状态,然后solve(mid+1,r),这就是一个分治。

为了使这个更新过程顺利完成,在solve(l,mid)时需要找到这个凸包,可以通过维护Xi的单调,然后直接一个栈保存单调的斜率即可;mid+1到r这一段,也需要保证-Ai/Bi的单调,这个可以预处理出来。现在就是一个离线问题,一边凸包单增被离线实现了,一边-Ai/Bi预处理排序好了,那就可以两个指针直接扫一遍更新了。

trick!!(Xj-Xk)可能等于0。。。。。。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 #include<math.h>
 6 //#include<iostream>
 7 using namespace std;
 8
 9 int n,s;
10 #define maxn 200011
11 double a[maxn],b[maxn],rt[maxn];
12
13 int num[20][maxn];
14 void mergesort(int L,int R,int cur)
15 {
16     if (L==R)
17     {
18         num[cur][L]=L;
19         return;
20     }
21     const int mid=(L+R)>>1;
22     mergesort(L,mid,cur+1),mergesort(mid+1,R,cur+1);
23     int i=L,j=mid+1,k=i;
24     while (i<=mid && j<=R)
25     {
26         if (-a[num[cur+1][i]]/b[num[cur+1][i]]>-a[num[cur+1][j]]/b[num[cur+1][j]])
27             num[cur][k++]=num[cur+1][i++];
28         else num[cur][k++]=num[cur+1][j++];
29     }
30     while (i<=mid) num[cur][k++]=num[cur+1][i++];
31     while (j<=R) num[cur][k++]=num[cur+1][j++];
32 }
33
34 double f[maxn],xx[maxn],yy[maxn];int sta[maxn],top,numf[20][maxn];
35 double calc(int i,int j) {return fabs(xx[i]-xx[j])>1e-9?(yy[i]-yy[j])/(xx[i]-xx[j]):1e300;}
36 //i>j
37 void solve(int L,int R,int cur)
38 {
39     if (L==R)
40     {
41         numf[cur][L]=L;
42         f[L]=max(f[L],f[L-1]);
43         yy[L]=f[L]/(rt[L]*a[L]+b[L]);
44         xx[L]=f[L]*rt[L]/(rt[L]*a[L]+b[L]);
45         return;
46     }
47     const int mid=(L+R)>>1;
48     solve(L,mid,cur+1);
49     top=0;
50     for (int i=L;i<=mid;i++)
51     {
52         const int id=numf[cur+1][i];
53         while (top>1 && calc(id,sta[top])-calc(sta[top],sta[top-1])>-1e-9) top--;
54         sta[++top]=id;
55     }
56     for (int i=mid+1,j=1;i<=R;i++)
57     {
58         const int &id=num[cur+1][i];
59         while (j<top && -a[id]/b[id]-calc(sta[j+1],sta[j])<1e-9) j++;
60         f[id]=max(f[id],a[id]*xx[sta[j]]+b[id]*yy[sta[j]]);
61     }
62     solve(mid+1,R,cur+1);
63     int i=L,j=mid+1,k=i;
64     while (i<=mid && j<=R)
65     {
66         if (xx[numf[cur+1][i]]<xx[numf[cur+1][j]]) numf[cur][k++]=numf[cur+1][i++];
67         else numf[cur][k++]=numf[cur+1][j++];
68     }
69     while (i<=mid) numf[cur][k++]=numf[cur+1][i++];
70     while (j<=R) numf[cur][k++]=numf[cur+1][j++];
71 }
72
73 int main()
74 {
75     scanf("%d%d",&n,&s);
76     for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&rt[i]);
77     mergesort(1,n,0);
78 //    for (int i=0;i<=2;i++){
79 //        for (int j=1;j<=n;j++)
80 //            cout<<(-a[num[i][j]]/b[num[i][j]])<<‘ ‘;cout<<endl;}
81     f[0]=s;for (int i=1;i<=n;i++) f[i]=0;
82     solve(1,n,0);
83 //    for (int i=1;i<=n;i++) cout<<f[i]<<‘ ‘;cout<<endl;
84     printf("%.3f\n",f[n]);
85     return 0;
86 }

时间: 2024-08-02 11:02:39

cdq分治入门--BZOJ1492: [NOI2007]货币兑换Cash的相关文章

[BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5838  Solved: 2345[Submit][Status][Discuss] Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金

bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5541  Solved: 2228[Submit][Status][Discuss] Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金

[BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5907  Solved: 2377[Submit][Status][Discuss] Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金

bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash

http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不操作 dp[i]=max(dp[i-1],X[j]*A[i]+Y[j]*B[i]) 其中X[j]表示在第j天能买多少A纪念券,Y[j]表示在第j天能买多少B纪念券 可列方程 X[j]*A[j]+Y[j]*B[j]=dp[j] 又因为 X[j]=Rate[j]*Y[j] 所以解出 Y[j]=dp[j]

BZOJ1492: [NOI2007]货币兑换Cash

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1492 f[i]=max(f[i-1],x[j]*a[i]+y[j]*b[i]) f[i]表示最大获利,x[j],y[j]分别表示第j天能够换到的最多的A,B券数量. 在斜率优化中如果每个状态给的斜率是单调的,凸包上的点x坐标,y坐标都是单调的..那上个单调队列就可以了.. 这题中给的斜率和凸包上的点x坐标都不是单调的... (因为不会写平衡树维护动态凸包所以写了cdq分治.. 其实...老老

bzoj1492: [NOI2007]货币兑换Cash(待更新)

1D1D动态规划问题.网上的大多都说有三类,然而我觉得吧,就是两类,一类利用单调性和斜率优化可以解决的,主要是因为能证明单调性(斜率与二元组的横坐标同时满足单调性,实际上很多时候都不用列二元组,你搞斜率优化的时候看得出来吧)那不单调就只能这样搞了. 暴力. /* O(n^2)暴力DP,f表示这天有多少人民币,转移时就转换成券来求 A表示A券价值,B表示B券价值,S表示这个券的数量 题意得:A*Sa+B*Sb=F,Sa/Sb=R (A*R+B)*Sb=F; Sb=F/(A*R+B); Sa=Sb*

【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治

[BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目.我们记录第 K 天中 A券 和 B券 的价值分别为 AK 和 BK(元/单位金券).为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易

【BZOJ-1492】货币兑换Cash DP + 斜率优化 + CDQ分治

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 3396  Solved: 1434[Submit][Status][Discuss] Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个实数.每天随着市场的起伏波动, 两种金券都有自己当时的价值,即每一单位金

bzoj [NOI2007]货币兑换Cash (cdq分治+斜率优化 )

1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 2454  Solved: 1078 [Submit][Status][Discuss] Description Input 第一行两个正整数N.S,分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来N 行,第K 行三个实数AK.BK.RateK,意义如题目中所述 Output 只有一个实数MaxProfit,表示第N 天的操作结束时能够获得的最大的