题目大意:
这题真是为难了我1B。。。做了三天,两种方法都试过了一遍,真是膜拜NOI的神犇们能在一个多小时内干掉这道题
首先我们分析一下操作 首先是卖出
假设我们手中有一堆A券和一堆B券 选择在一些天数分天卖出
那么这些天中一定有一天,卖出同样比例的证券可以获得的钱最多
我们选择这一天全部卖出 一定比分天卖出更优
然后是买入 由于卖出是一天 对于任意一天卖出 我分开买 那么一定有一天花同样的钱买入证券后在那一天卖出获利最大
选择在那一天全部买入 一定比分天买入更优
故买入和卖出都是在一天完成,而且都是倾巢买入/卖出
然后。。这题一看就是斜率优化 连递推式都是P=A[i]*X[i]+B[i]*Y[i] 万事俱备 就是AB不单调!
斜率不单调其实也好办 反正是凸包 平衡树维护不就简单了
简单个熊啊!!!
第一天下午开始写,第二天早上还在调,下午才AC,写了足足5.5KB。。
SPLAY各种挂 调的我都快被SPLAY了
此外这题递推式不满足队列优化的条件、、只能在平衡树上找斜率,着实坑了我1B。。。
然后是代码 边界讨论那里写的有点累赘 见谅了
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 100100 #define null (&empty) using namespace std; typedef pair<double,double> point; struct List{ point p; double slope; List *last,*next; }head; typedef pair<int,List*> P; struct abcd{ point num; List *pos; int size; abcd*ls,*rs; void sizmaintain(){ size=1; if(ls)size+=ls->size; if(rs)size+=rs->size; } }empty,*root=null,*Tar; void zig(abcd*&x) { abcd*k=x->ls; x->ls=k->rs; k->rs=x; x=k; x->rs->sizmaintain(); x->sizmaintain(); } void zag(abcd*&x) { abcd*k=x->rs; x->rs=k->ls; k->ls=x; x=k; x->ls->sizmaintain(); x->sizmaintain(); } void splay(abcd *&x,abcd **y) { if(x==Tar)return ; if(x==*y) { if(x->ls==Tar){ zig(x); return ; } else if(x->rs==Tar){ zag(x); return ; } } if(x->ls->ls==Tar) zig(x),zig(x); else if(x->rs->rs==Tar) zag(x),zag(x); else if(x->ls->rs==Tar) zag(x->ls),zig(x); else if(x->rs->ls==Tar) zig(x->rs),zag(x); } void insert(abcd*&x,point y,List *z) { Tar=x; if(x==null){ Tar=x=new abcd; x->num=y; x->pos=z; x->size=1; x->ls=x->rs=null; return ; } x->size++; if(y<x->num)insert(x->ls,y,z); else insert(x->rs,y,z); splay(x,&root); } void update(abcd*&x,point y,abcd**z) { int re=x->ls->size; if(y==x->num) { Tar=x; return ; } if(y<x->num) update(x->ls,y,z); else update(x->rs,y,z); splay(x,z); } List* pre(abcd*x,point y) { if(x==null)return &head; if(y<=x->num)return pre(x->ls,y); List*z=pre(x->rs,y); if(z->p<x->num) return x->pos; return z; } double getslope(point p1,point p2) { if(p1.first==p2.first) return 2147483647*(p2.second>p1.second?1:-1); return (double)( p2.second-p1.second )/( p2.first-p1.first ); } void insert_point(point p) { if(!head.next) { List*temp=new List; head.next=temp; temp->p=p; temp->next=NULL; temp->last=&head; temp->slope=2147483647; insert(root,p,temp); return ; } List*pos=pre(root,p); if( pos->next && pos->next->p == p ) return; if(pos==&head) { pos=head.next; while(1) { pos->slope = getslope( p , pos->p ); if(!pos->next) break; if( pos->next->slope > pos->slope ) pos=pos->next; else break; } List*temp=new List; temp->next=pos; pos->last=temp; head.next=temp; temp->last=&head; temp->p=p; temp->slope=2147483647; update(root,temp->next->p,&root); root->ls=null; root->sizmaintain(); insert(root,p,temp); return ; } if(!pos->next) { List*temp=new List; while(1) { temp->slope = getslope( pos->p , p ); if(pos->last==&head) break; if( temp->slope > pos->slope ) pos=pos->last; else break; } pos->next=temp; temp->last=pos; temp->p=p; temp->next=NULL; update(root,pos->p,&root); root->rs=null; root->sizmaintain(); insert(root,p,temp); return ; } List*pos2 = pos->next; if( getslope(pos->p,p) < getslope(p,pos2->p) ) return ; List*temp = new List; while(1) { pos2->slope=getslope(p,pos2->p); if(!pos2->next) break; if( pos2->next->slope > pos2->slope ) pos2=pos2->next; else break; } while(1) { temp->slope = getslope( pos->p , p ); if(pos->last==&head) break; if( temp->slope > pos->slope ) pos=pos->last; else break; } temp->p=p; temp->next=pos2; pos2->last=temp; temp->last=pos; pos->next=temp; update(root,pos->p,&root); update(root,pos2->p,&(root->rs) ); root->rs->ls=null; root->rs->sizmaintain(); root->sizmaintain(); insert(root,p,temp); } List* getans(abcd *x,double y)//找到第一个斜率大于y的点 { head.next->slope=2147483647; if(x==null)return head.next; double s = x->pos->slope; if(y>=s)return getans(x->ls,y); List*z=getans(x->rs,y); if(z->slope>s)return x->pos; return z; } /* List* pre(abcd*x,point y) { if(x==null)return &head; if(y<=x->num)return pre(x->ls,y); List*z=pre(x->rs,y); if(z->p<x->num) return x->pos; return z; } */ double A[M],B[M],Rate[M],X[M],Y[M],f; int n; int main() { int i; //freopen("cash.in","r",stdin); //freopen("cash.out","w",stdout); empty.ls=empty.rs=null; cin>>n>>f; for(i=1;i<=n;i++) scanf("%lf%lf%lf",&A[i],&B[i],&Rate[i]); for(i=1;i<=n;i++) { if(i!=1) { point p=getans( root , - A[i] / B[i] )->p; f=max( f , A[i] * p.first + B[i] * p.second ); } X[i] = f / ( A[i] + B[i] / Rate[i] ); Y[i] = f / ( A[i] * Rate[i] + B[i] ); insert_point( make_pair( X[i] , Y[i] ) ); //printf("%d %.3lf %.3lf %.3lf\n",i,f,X[i],Y[i]); } printf("%.3lf\n",f); }
第二种方法就简单多了 CDQ分治
CDQ分治的思想是f[i]的决策点一定在0~i-1之中,于是我们分治,其中对于每一层递归[l,r],二分出一个中点mid
然后递归求出[l,mid]中所有的f值,对[l,mid]中的点建立凸包,用[mid+1,r]中的所有元素的斜率去询问,得到[l,mid]中所有的点对[mid+1,r]中所有点的影响
然后递归求出[mid+1,r]中的所有f值
其中当我们递归到任意[i,i]时,0~i-1中所有点对这个点的影响都已经计算过,于是直接得出f[i]即可
这个算法不难理解,但是我挂了半天。。CDQ没写错,sort居然写错了,按照Rate排了个序,眼科大夫死得早啊
最可怕的是前四个点居然还过了 这是要有多水的节奏
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 100100 using namespace std; typedef pair<double,double> P; int n,stack[M],top; double f[M]; struct abcd{ double A,B,Rate; double slope; int pos; }q[M],nq[M]; bool operator < (abcd x,abcd y) { return x.slope < y.slope; } P p[M],np[M]; double getslope(P x,P y) { if(x.first==y.first) return 2147483647*(y.second>=x.second?1:-1); return (y.second-x.second)/(y.first-x.first); } void CDQ(int l,int r) { int i,mid=l+r>>1; if(l==r) { f[mid]=max( f[mid] , f[mid-1] ); p[mid].first = f[mid] / ( q[mid].A + q[mid].B / q[mid].Rate ) ; p[mid].second = f[mid] / ( q[mid].A * q[mid].Rate + q[mid].B ) ; return ; } int l1=l,l2=mid+1; for(i=l;i<=r;i++) if(q[i].pos<=mid) nq[l1++]=q[i]; else nq[l2++]=q[i]; memcpy( q+l , nq+l , sizeof(q[0])*(r-l+1) ); CDQ(l,mid); top=0; for(i=l;i<=mid;i++) { while(top>1) if( getslope(p[stack[top-1]],p[stack[top]]) < getslope(p[stack[top]],p[i]) ) stack[top--]=0; else break; stack[++top]=i; } for(i=mid+1;i<=r;i++) { while(top>1) if( getslope(p[stack[top-1]],p[stack[top]]) < q[i].slope ) stack[top--]=0; else break; f[q[i].pos]=max(f[q[i].pos],q[i].A*p[stack[top]].first+q[i].B*p[stack[top]].second); } CDQ(mid+1,r); l1=l;l2=mid+1; for(i=l;i<=r;i++) if ( ( p[l1]<p[l2] || l2>r ) && l1<=mid ) np[i]=p[l1++]; else np[i]=p[l2++]; memcpy( p+l , np+l , sizeof(p[0])*(r-l+1) ); } int main() { int i; //freopen("cash.in","r",stdin); //freopen("cash.out","w",stdout); cin>>n>>f[0]; for(i=1;i<=n;i++) { scanf("%lf%lf%lf",&q[i].A,&q[i].B,&q[i].Rate); q[i].slope=-q[i].A/q[i].B; q[i].pos=i; } sort(q+1,q+n+1); CDQ(1,n); printf("%.3lf\n",f[n]); }