码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj1492: [NOI2007]货币兑换Cash

时间:2016-04-16 18:29:40      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:

cdq分治,dp。这道题太难了,我看了一上午一点头绪也没有//蒟蒻本性

连这篇题解都是边做边写的,要不就忘了(雾)。。

维护的是上凸包。

2.30 pm 终于过了,照着人家的题解打,居然都交了10多遍。//蒟蒻本性

首先,不难得到这样的结论:如果买就要把所有的钱都用完(买能挣钱,就要多买,不如其他的挣钱就买别的)。如果卖,就要都卖掉。

用数组a保存a[i],b[i],rate[i]。

用f[i].x代表第x天能买到最多的a物品,f[i].y代表最多的b物品。则有f[i].x = f[i].y*a[i].r。

用v[i]代表第i天能得到最大的收益,则有v[i] = max(v[i]-1,f[i].x*a[i]+f[i].y*b[i])。

暴力枚举O(n^2),tle。

所以需要维护一个上凸包。

在第i天时,假设f[j].x<=f[k].x,如果决策j优于k,则有 (f[i].x-f[j].x)*a[i].a+(f[j].y-f[k].y)*a[i].b>0。

经过移项,就有 (f[j].y-f[j].y)/(f[j].x-f[j].x) < –a[i].a/a[i].b。

先记住这个结论,那么利用呢?

1.在预处理时先按(-a[i]/b[i])降序排序。

2/然后依靠平衡树(我不会。。)和cdq分治(我抄的。。)了。

cdq分治:首先先把要处理的区间分成俩部分,先递归处理左区间,然后计算右区间,最后递归处理右区间。

这样有什么好处呢? 就是在计算一天的dp值的时候,也处理了后面所有天数的dp值的一部分。

为什么呢?如果我们按单调性排序了以后,维护出一个下凸包。

在处理第j天时,假设第k天为决策最优,这样k天以前的值在第j天以后的决策中都没用了。

因为满足 小于 –a[j].a/a[j].b,必定小于–a[h].a/a[h].b,h>j。因为排序过了。

分治一共logn层,每层遍历n个数,总复杂度O(nlogn)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 100000 + 10;

int n,m,p[maxn],s[maxn];
struct Point {
    double x,y;
}f[maxn],t[maxn];
struct DAY {
    double a,b,r;
} a[maxn];
double v[maxn];

bool operator < (Point a,Point b) {
    return a.x < b.x || (a.x==b.x&&a.y<b.y);
}

bool cmp(int x,int y) {
    return a[x].b/a[x].a > a[y].b/a[y].a;
}

bool dir(Point a,Point b,Point c) {
    return ((b.x-a.x)*(c.y-b.y)-(b.y-a.y)*(c.x-b.x))>=0;
}

double calc(Point f,int i) {
    return f.x*a[i].a + f.y*a[i].b;
}

void cdq(int l,int r,double m) {
    if(l == r) {
        v[l] = max(v[l],m);
        f[l].y = v[l]/(a[l].a*a[l].r+a[l].b);
        f[l].x = a[l].r*f[l].y;
        return;
    }
    
    int mid = (l+r)/2,m1=l,m2=mid+1,L=0,R=0;
    for(int i=l;i<=r;i++) {
        if(p[i]<=mid) s[m1++]=p[i];
        else s[m2++]=p[i];
    }
    memcpy(p+l,s+l,sizeof(int)*(r-l+1)); 
    cdq(l,mid,m);
    for(int i=l;i<=mid;i++) {
        while(R>1 && dir(t[R-2],t[R-1],f[i])) R--;
        t[R++] = f[i]; 
    }
    
    for(int i=mid+1;i<=r;i++) {
        while(L<R-1 && calc(t[L],p[i])<calc(t[L+1],p[i])) ++L;
        v[p[i]] = max(v[p[i]],calc(t[L],p[i]));
    }
    cdq(mid+1,r,v[mid]);
    
    merge(f+l,f+mid+1,f+mid+1,f+r+1,t);
    memcpy(f+l,t,sizeof(Point)*(r-l+1));
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].r);
        p[i] = i;
    }
    sort(p+1,p+n+1,cmp);
    cdq(1,n,m);
    printf("%.3lf\n",v[n]);
    return 0;
}

 

bzoj1492: [NOI2007]货币兑换Cash

标签:

原文地址:http://www.cnblogs.com/invoid/p/5398401.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!