【CDQ分治】NOI2007货币兑换Cash

分析:

非常经典的CDQ版题。。。不知道为什么之前没写博客。。现在补上。

首先,不难得到一个会T的DP,定义 D P [ i ] 表示前i天能得到的最大钱数。

D P [ i ] = m a x { D P [ j ] A i + D P [ j ] / r a t e j B i }
即表示在第j天把所有钱换成金券,在第i天再全部换成钱。

在贪心的思路下,这个应该还是算显然的。

然后考虑斜率优化,定义 j < k j 优于 k
那么

D P [ j ] A i + D P [ j ] / r a t e j B i > D P [ k ] A i + D P [ k ] / r a t e k B i

( D P [ j ] D P [ k ] ) A i > ( D P [ j ] / r a t e j D P [ k ] / r a t e k ) B i

A i B i > ( D P [ j ] / r a t e j D P [ k ] / r a t e k ) ( D P [ j ] D P [ k ] )

然而。。我们发现这个式子就非常恶心了。。。 A i B i 不是单调的!

所以呢,引入CDQ分治来解决。

首先,根据可以先根据 A i B i 排个序。

然后呢,分段处理DP

每一段先按照 i 归并排序。

然后用单调队列处理右半区间的所有DP值,此时可以保证左侧的 A i B i 升序,所以就用普通的斜率优化的格式就可以处理了。

对了,这题要注意必须先处理左半区间,再处理当前区间的转移,再处理右半区间。因为本题中Dp值需要先左边更新了右边,才能得到右边的位置真正的DP值。(即如果先处理右半区间,再处理当前区间的话,可能会使得处理右侧时的DP值不是最优的)

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 0x3FFFFFFF
#define EPS 1e-9
using namespace std;
int n;
double s;
struct node{
    double x,y,a,b,k,rate;
    int w,id;
    bool operator < (const node &a)const {
        return a.k<k;
    }
}p[MAXN],t[MAXN];
int st[MAXN],top;
double f[MAXN];
double getk(int a,int b){
    if(b==0)
        return -INF;
    if(fabs(p[a].x-p[b].x)<EPS) return INF;
    return (p[b].y-p[a].y)/(p[b].x-p[a].x);
}
void solve(int l,int r){
    if(l==r){
        f[l]=max(f[l],f[l-1]);
        p[l].y=f[l]/(p[l].a*p[l].rate+p[l].b);
        p[l].x=p[l].rate*p[l].y;
        return ;
    }
    int l1,l2,mid=(l+r)>>1,j=1;
    l1=l;
    l2=mid+1;
    for(int i=l;i<=r;i++){
        if(p[i].id<=mid)
            t[l1++]=p[i];
        else
            t[l2++]=p[i];
    }
    for(int i=l;i<=r;i++)
        p[i]=t[i];
    solve(l,mid);
    top=0;
    for(int i=l;i<=mid;i++){
        while(top>1&&getk(st[top-1],st[top])<getk(st[top-1],i)+EPS)
            top--;
        st[++top]=i;
    }
    st[++top]=0;
    for(int i=mid+1;i<=r;i++){
        while(j<top&&getk(st[j],st[j+1])+EPS>p[i].k)
            j++;
        f[p[i].id]=max(f[p[i].id],p[st[j]].x*p[i].a+p[st[j]].y*p[i].b);
    }
    solve(mid+1,r);
    l1=l;
    l2=mid+1;
    for(int i=l;i<=r;i++){
        if(((p[l1].x<p[l2].x||(fabs(p[l1].x-p[l2].x)<EPS&&p[l1].y<p[l2].y))||l2>r)&&l1<=mid)
            t[i]=p[l1++];
        else
            t[i]=p[l2++];
    }
    for(int i=l;i<=r;i++)
        p[i]=t[i];
}
int main(){
    SF("%d%lf",&n,&f[0]);
    for(int i=1;i<=n;i++){
        SF("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].rate);
        p[i].k=-p[i].a/p[i].b;
        p[i].id=i;
    }
    sort(p+1,p+1+n);
    //PF("{%.3f %.3f}\n",p[1].k,p[2].k);
    solve(1,n);
    PF("%.3lf",f[n]);
}

猜你喜欢

转载自blog.csdn.net/qq_34454069/article/details/81273271
今日推荐