BZOJ 1492 货币兑换 Cash 平衡树维护凸包 CDQ分治

27 篇文章 0 订阅
12 篇文章 0 订阅

题目大意:


这题真是为难了我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]);
}

最后还是来一句膜拜神犇吧 07年的第一题就这么难 这么难 这么难0.0.0.0.0.0.0.0.0.0
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值