1. 程式人生 > >bzoj 1492: [NOI2007]貨幣兌換Cash

bzoj 1492: [NOI2007]貨幣兌換Cash



測試資料設計使得精度誤差不會超過10-7。
對於40%的測試資料,滿足N ≤ 10;
對於60%的測試資料,滿足N ≤ 1 000;
對於100%的測試資料,滿足N ≤ 100 000;

動態維護凸包+斜率優化

CDQ分治+斜率優化

剛好對於前面正在做的CDQ分治和斜率優化用這題和NOI2014購票來做個總結好了

首先明確一點。要使獲利最大。要麼全買要麼全賣

那麼就可以得出狀態轉移方程了

f[i]=max{f[j]/(a[j]*rate[j]+b[j])*rate[j]*a[i]+f[j]/(a[j]*rate[j]+b[j])*b[i]}

其中,x[j]=f[j]/(a[j]*rate[j]+b[j])*rate[j]表示第j天最多可以擁有的A貨幣的數量

   y[j]=f[j]/(a[j]*rate[j]+b[j])表示第j天最多可以擁有的B貨幣的數量

然後可以得出斜率方程 y[i]=(-a[i]/b[i])*x[i]+f[i]/b[i]

可我們發現這個斜率沒有單調性。怎麼辦?

方法一:用平衡樹動態維護凸包,然後更新的時候二分。

方法二:CDQ分治

對於區間(l,r),(l,mid)按照x排序,(mid+1,r)按照斜率排序

然後對於前面的點我們維護一個凸包。後面的點順著更新就可以了

*關於分治內部要歸併排序。如果圖省事用快排的話會T掉兩個點。而n^2的暴力轉移也可以拿60分

【程式最後附測試資料一組】

#include<cstdio>
#include<algorithm>
using namespace std;
double eps=1e-9;
struct cash
{
     double a,b,rate;
     double x,y;
     double k;
     double f;
     int p;
}a[100001],qx[100001];
int q[100001];
double f[100001];
inline double absx(double x)
{
     if(x<0)
          x=-x;
     return x;
}
inline double getk(int j,int k)
{
	 if(absx(a[k].x-a[j].x)<=eps)
	      return -2100000000;
     return (a[k].y-a[j].y)/(a[k].x-a[j].x);
}
inline bool cmp1(cash x,cash y)
{
     if(x.x<y.x||x.x==y.y&&x.x>y.y)
          return true;
     return false;
}
inline bool cmp2(cash x,cash y)
{
     if(x.k>y.k)
          return true;
     return false;
}
inline bool cmp3(cash x,cash y)
{
     if(x.p<y.p)
          return true;
     return false;
}
inline void solve(int l,int r)
{
     int mid=(l+r)/2;
     if(l!=r)
     {
          int p1=l-1,p2=mid;
          int i,p=0;
          for(i=l;i<=r;i++)
          {
               if(a[i].p<=mid)
               {
                    p1++;
                    qx[p1]=a[i];
               }
               else
               {
                    p2++;
                    qx[p2]=a[i];
               }
          }
          for(i=l;i<=r;i++)
               a[i]=qx[i];
          solve(l,mid);
          for(i=l;i<=mid;i++)
          {
               while(p>1&&getk(q[p-1],q[p])<getk(q[p],i))
                    p--;
               p++;
               q[p]=i;
          }
          int d=1;
          for(i=mid+1;i<=r;i++)
          {
               while(d<p&&a[i].k<getk(q[d],q[d+1]))
                    d++;
               int j=q[d];
               f[a[i].p]=max(f[a[i].p],a[j].x*a[i].a+a[j].y*a[i].b);
               a[i].y=f[a[i].p]/(a[i].a*a[i].rate+a[i].b);
               a[i].x=a[i].y*a[i].rate;
          }
          solve(mid+1,r);
          p=l-1;
          int l1=l,l2=mid+1;
          while(l1<=mid&&l2<=r)
          {
               if(a[l1].x<a[l2].x||a[l1].x==a[l2].x&&a[l1].y>a[l2].y)
               {
                    p++;
                    qx[p]=a[l1];
                    l1++;
               }
               else
               {
                    p++;
                    qx[p]=a[l2];
                    l2++;
               }
          }
          if(l1<=mid)
          {
               for(i=l1;i<=mid;i++)
               {
               	    p++;
                    qx[p]=a[i];
               }
          }
          else
          {
               for(i=l2;i<=r;i++)
               {
               	    p++;
                    qx[p]=a[i];
               }
          }
          for(i=l;i<=r;i++)
               a[i]=qx[i];
     }
     else
     {
          f[l]=max(f[l-1],f[l]);
          a[l].y=f[l]/(a[l].a*a[l].rate+a[l].b);
          a[l].x=a[l].y*a[l].rate;
     }
}
int main()
{
     int n;
     double s;
     scanf("%d%lf",&n,&s);
     f[0]=s;
     int i;
     for(i=1;i<=n;i++)
     {
          scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].rate);
          a[i].k=-a[i].a/a[i].b;
          a[i].p=i;
     }
     sort(a+1,a+1+n,cmp2);
     solve(1,n);
    // for(i=1;i<=n;i++)
     printf("%.3lf\n",f[n]);
     return 0;
}
/*
10 100 
5.226 5.381 1.73 
5.273 5.899 1.35 
5.275 5.236 1.93 
5.769 5.863 1.78 
5.888 5.064 1.91 
5.464 5.894 1.95 
5.565 5.731 1.97 
5.568 5.305 1.31 
5.639 5.501 1.85 
5.751 5.925 1.14 
*/